Por qué las value classes de Valhalla no son magia: lo que Johan Sjölen aprendió peleándose con C2
Cuando un ingeniero de la JVM te dice que declares value class con cuidado y no «a lo loco por si acaso», conviene escucharlo. Johan Sjölen, desarrollador del equipo del compilador C2 en OpenJDK, publicó el 26 de agosto un post en su blog personal, Value Classes Still Need Compiler Sympathy, que se ha convertido en la lectura obligada de la semana para cualquiera que trabaje con Java. El mensaje central: las value classes de Project Valhalla llegan como preview en JDK 28 y prometen mucho, pero usarlas sin pensar puede dejar tu código igual o peor que antes.
Para un founder hispanohablante esto importa menos por el código en sí y más por lo que señala sobre cómo evoluciona la plataforma donde corre buena parte del software empresarial y fintech del mundo.
Qué cambia con JEP 401 en JDK 28
JEP 401, Value Classes and Objects, se integró en JDK 28 como feature en preview, según confirmó la ingeniera de Oracle Lois Foltan en la lista de correo de OpenJDK. El cambio suma más de 197.000 líneas de código en 1.816 ficheros, una cifra que The Next Web destacó como la mayor sacudida al modelo de objetos de Java en una década.
👥 ¿Quieres ir más allá de la noticia?
En nuestra comunidad discutimos las tendencias, compartimos oportunidades y nos ayudamos entre emprendedores. Sin humo, solo acción.
👥 Unirme a la comunidadHasta ahora, prácticamente todo en Java era un reference type: cada new producía un objeto con identidad única en memoria. Con JEP 401 puedes declarar clases con el modificador value — por ejemplo value record Point(int x, int y) {} — y sus instancias se distinguen solo por el valor de sus campos, no por su dirección de memoria. Esto le da a la JVM libertad para aplanar el objeto, escalarizarlo (representar sus componentes en registros del CPU) o directamente no asignarle memoria.
Según InfoQ, al activar el preview 30 clases del JDK ya pasan a ser value classes de forma automática, entre ellas Integer, Long, Double, Boolean, Optional, LocalDate, LocalDateTime y Duration.
La trampa que señala Sjölen
Sjölen desmonta la idea de «ponle value a todo y ya». El problema: una misma clase puede requerir dos representaciones distintas según el contexto. A veces aplanar es más rápido; a veces mantener una referencia lo es. Cuando esos dos mundos coexisten en el mismo programa, la JVM se ve forzada a convertir entre representaciones, con el coste que eso implica.
Lo muestra con un ejemplo muy concreto. El record FourLongs(long a, b, c, d) ocupa 32 bytes, demasiado para una actualización atómica aplanada. Si lo guardas como record Envelope(FourLongs payload), donde payload es un campo final inicializado de forma estricta (JEP 539, Strict Field Initialization in the JVM), el runtime aplana el valor. Si en cambio lo guardas en una clase mutable, vuelve a representación por referencia. La diferencia, según explica en el post, la dicta el Java Memory Model: escribir un valor aplanado componente a componente puede generar lecturas tearing si dos hilos escriben a la vez, y eso el lenguaje lo prohíbe.
La lección no es nueva: inmutabilidad + campos final son el pase para que la JVM optimice de verdad.
C2 puede hacer cosas sorprendentes… y otras que no
El segundo caso del post es un bucle for que llama a una función que «parece» asignar memoria en cada iteración (new FourLongs(...)). Con value classes, el compilador C2 detecta que lo único que cambia es el componente a y reescribe el bucle a:
mov x0, #9
cmp x1, #0
b.le done
add x0, x0, w1, sxtw
done:
ret
Es decir, cero asignaciones, cero objetos, resultado directo: iterations + 9. Al convertir el FourLongs en un identity record, la misma optimización desapareció. C2 no pudo demostrar que la identidad no importaba y dejó las asignaciones dentro del bucle.
«Providing the compiler with stronger semantic guarantees can sometimes produce a very big win» — Johan Sjölen
Esto es exactamente lo que promete Valhalla: cuanto más semántica le das a la JVM, más te optimiza. Pero el matiz es que el compilador no siempre puede recuperar esa información cuando se pierde detrás de una abstracción.
Cuando la genericidad rompe la magia
La parte más jugosa del post es el tercer caso. Un usuario en valhalla-dev portó una librería de parsing de Elm a Java y notó una regresión de rendimiento al convertir sus records a value records. ¿Cómo es posible que dar más información haga el código más lento?
El culpable: type erasure. Java implementa generics borrando los tipos en tiempo de ejecución. Si defines interface Frobber extends Fun<Carrier, LargeValue>, la JVM solo ve Object apply(Object). El compilador genera bridge methods que castean el argumento y la salida. Cuando C2 encuentra un call site megamórfico (tres o más implementaciones distintas pasándose por la misma interfaz), no puede desvirtualizar y termina invocando el bridge. El bridge tiene que materializar el valor en heap, llamar a la versión tipada, y volver a materializar la salida, exactamente la operación que las value classes quieren evitar.
En el reproductor, esa versión borrada megamórfica asignaba 192 bytes por invocación de la función reproduce. El fix de Sjölen, que parece trivial pero es revelador, es redeclarar el método tipado en la interfaz:
interface Frobber extends Fun<Carrier, LargeValue> {
@Override
Carrier apply(LargeValue value);
}
Con esa línea extra, el descriptor en el class file pasa de Object apply(Object) a Carrier apply(LargeValue), el bridge desaparece y el call site megamórfico ya no necesita materializar nada. Resultado: 0 bytes asignados por invocación.
¿Por qué debería importarle a un founder?
Aunque escribas Python o JavaScript en el día a día, Java sigue siendo la columna vertebral de banca, telecomunicaciones, ecommerce y buena parte del SaaS B2B que sostiene tu stack. Project Valhalla lleva desde 2014 en desarrollo, según The Next Web, y el cambio que llega en JDK 28 es el primer paso público. Brian Goetz, Java Language Architect en Oracle, fue claro en Reddit: JEP 401 es «solo la primera parte de Valhalla». La salida de preview para el próximo LTS (JDK 29, previsto para septiembre de 2027) la ve «optimista».
Tres implicaciones prácticas para founders que contratan equipos Java o mantienen sistemas sobre la JVM:
- Audita qué código asume identidad antes de migrar. Sincronizar sobre
Integer, comparar wrappers con==esperando resultados no triviales, o cachear referencias esperando que==distinga dos instancias con el mismo contenido: todo eso cambia con value classes. El JEP ya avisa de breaking changes deliberados. - Trata
valuecomo una decisión semántica, no de rendimiento. Si tu clase representa un dato puro (coordenadas, importes, timestamps) decláralavaluepor claridad. La ganancia de rendimiento es una consecuencia, no una garantía: depende de cómo el compilador JIT decida representarla. - Mide en producción con el preview activado. Las migraciones de
Integer,LocalDatey compañía ya están pasando. Si tu servicio depende de identity-based caching o reflexión agresiva, enciende--enable-previewen un entorno de staging y compara antes de que te explote en producción.
El consejo final: profiler primero, intuición después
Sjölen cierra su post con una recomendación que vale para cualquier lenguaje y cualquier runtime moderno: «Profiling and inspecting the generated code remain the best ways to understand what is happening». Las herramientas que el autor lista al final (-XX:+PrintAssembly, CompileCommand=print, builds con desensamblador) están al alcance de cualquier equipo que quiera verificar de primera mano qué hace el compilador con su código.
Si trabajas en una startup con servicios JVM de alta carga, este es el momento perfecto para bajar una early-access build de JDK 28 y empezar a experimentar. No esperes al release final: cuando el lenguaje cambie, quienes entiendan el modelo de objetos serán los que menos roto tengan.
Fuentes
- Value Classes Still Need Compiler Sympathy — Johan Sjölen (fuente original)
- Java's biggest language change in a decade is finally landing — The Next Web
- Java Is About to Change What an Object Can Be — ADT Mag
- Project Valhalla's First Preview: JEP 401 Redefines == for Java Objects — InfoQ
👥 ¿Quieres ir más allá de la noticia?
En nuestra comunidad discutimos las tendencias, compartimos oportunidades y nos ayudamos entre emprendedores. Sin humo, solo acción.
👥 Unirme a la comunidad













