Por Canuto  

Project Valhalla por fin entró a la ruta oficial de OpenJDK y apunta a JDK 28, un paso que muchos en la comunidad Java llegaron a considerar improbable. La novedad no es menor: JEP 401 introduce clases y objetos de valor, una base para que Java almacene datos de forma más densa y eficiente sin renunciar a la legibilidad de las clases tradicionales.
***

  • JEP 401 fue confirmado para integración en OpenJDK y apunta a JDK 28 como función de vista previa desactivada por defecto.
  • Las nuevas clases de valor prometen código con semántica de clase y eficiencia cercana a los primitivos, gracias a scalarización y aplanamiento.
  • El debut no incluye todavía tipos no nulos ni genéricos especializados completos, por lo que la visión total de Valhalla llegará en varias versiones.


Project Valhalla finalmente dio el paso que durante años pareció inalcanzable para buena parte de la comunidad Java. El 15 de junio, la ingeniera de Oracle Lois Foltan confirmó que JEP 401, dedicado a clases y objetos de valor, se integrará en el repositorio principal de OpenJDK con objetivo en JDK 28.

La magnitud del cambio explica por qué el anuncio generó tanto ruido entre desarrolladores, arquitectos y equipos de infraestructura. La solicitud de extracción suma más de 197.000 líneas de código en 1.816 archivos, y llevó a pedir a otros committers que evitaran grandes cambios durante la integración.

El entusiasmo, sin embargo, vino acompañado de una advertencia importante desde el primer momento. Brian Goetz aclaró que lo que llega a JDK 28 es solo una vista previa, desactivada por defecto, y además apenas la primera parte de Valhalla.

Esa precisión importa porque el proyecto arrastra una historia de más de una década, múltiples rediseños y expectativas a veces desbordadas. Lo que se integrará no representa la promesa completa de unificar objetos y primitivas en Java, pero sí abre la puerta más concreta hasta ahora hacia esa meta.

Según explicó la publicación JVM Weekly, el avance tiene un peso técnico e histórico difícil de exagerar. La razón es simple: Java empieza a moverse contra una suposición que lo acompañó desde 1995, la idea de que todo objeto necesariamente tiene identidad.

Qué problema intenta resolver Valhalla

Para entender por qué este cambio es relevante, conviene recordar una tensión antigua en Java. Salvo por sus ocho tipos primitivos, casi todo en el lenguaje ha funcionado como tipo de referencia, lo que significa que las variables apuntan a objetos ubicados en otra parte de la memoria.

Ese diseño facilitó una programación orientada a objetos legible y segura, pero también introdujo costos considerables de rendimiento. Cada objeto necesita encabezados con metadatos, asignación en heap, trabajo del recolector de basura y accesos indirectos a memoria mediante punteros.

El problema se agrava cuando la escala sube y aparecen arreglos enormes de objetos simples. Un millón de instancias de una clase como Point no se almacenan como un bloque compacto de datos, sino como un millón de referencias que apuntan a objetos dispersos en memoria.

Ese patrón afecta la localidad de referencia y castiga a las cachés del procesador. En hardware moderno, donde la CPU avanzó mucho más rápido que la memoria principal, esos saltos por punteros pueden volver una operación de lectura decenas o hasta cientos de veces más costosa.

Valhalla nace para cerrar esa brecha entre el modelo de programación y la realidad del hardware actual. Su lema histórico, “code like a class, works like an int”, resume la apuesta: permitir clases legibles con validación y métodos, pero con comportamiento de almacenamiento y acceso mucho más cercano al de un primitivo.

Una década de prototipos, recortes y cambios de rumbo

Project Valhalla comenzó oficialmente en 2014, aunque la idea es más antigua que eso. James Gosling llegó a describir el esfuerzo como “seis PhDs atados en un solo nudo”, una frase que retrata tanto la ambición como la complejidad del trabajo.

Los primeros años estuvieron marcados por exploración más que por ejecución lineal. El equipo construyó cinco prototipos distintos y descartó varias ideas centrales antes de llegar al modelo que ahora desemboca en JEP 401.

Uno de los enfoques tempranos fue el llamado “Q-World”, que trataba a los nuevos tipos de valor como una categoría radicalmente distinta de los objetos. Esa vía prometía cercanía conceptual con los primitivos, pero también duplicaba complejidades en descriptores, bytecodes y reglas del sistema de tipos.

El avance decisivo llegó con “L-World”, un prototipo que permitió a los tipos de valor compartir el mismo transportador “L” de las referencias tradicionales. Esa unificación resultó menos problemática de lo esperado y abrió un camino mucho más sostenible para la JVM.

De esa etapa surgió una lección que terminó guiando el proyecto completo. El modelo interno de la máquina virtual no necesita coincidir por completo con el modelo que ve el programador, y esa separación permitió simplificar el lenguaje sin renunciar al rendimiento potencial.

También hubo una larga montaña rusa de nombres y conceptos. El proyecto pasó por expresiones como “tipos de valor”, “clases en línea” y “clases primitivas”, esta última ligada a un modelo de dos proyecciones que distinguía entre una variante de valor y otra de referencia.

Ese dualismo se consideró potente, pero demasiado pesado para el uso diario. Al final, OpenJDK optó por una formulación más limpia: clases de valor y objetos de valor como tipos de referencia sin identidad, mientras el tema de la no nulidad quedó separado en otro esfuerzo futuro.

Qué trae exactamente JDK 28 con JEP 401

La novedad central es la posibilidad de declarar una clase de valor con el modificador value. En términos prácticos, eso permite crear clases que siguen viéndose como clases normales, pero cuyas instancias carecen de identidad.

Estas clases mantienen métodos, validaciones e interfaces, pero operan bajo restricciones específicas. Sus campos de instancia son implícitamente finales, sus métodos no pueden ser sincronizados, y no pueden heredar de clases con identidad, aunque sí implementar interfaces.

La ausencia de identidad cambia varias reglas conocidas en Java. Dos objetos de valor creados por separado con el mismo contenido pueden considerarse equivalentes mediante ==, porque en este nuevo contexto ese operador pasa a evaluar sustituibilidad y no dirección de memoria.

Esa redefinición no elimina la utilidad de equals. El propio diseño aclara que == observa el estado interno de forma recursiva, mientras que equals sigue siendo la mejor opción cuando se quiere expresar igualdad semántica de datos.

Otra consecuencia es que synchronized deja de tener sentido sobre estos objetos. Si el código intenta sincronizar sobre una instancia de una clase de valor, la operación termina en una IdentityException, y para casos donde se necesite verificar identidad, aparecen ayudas como Objects.requireIdentity y Objects.hasIdentity.

Un punto que puede sorprender a muchos desarrolladores es que estas clases aún pueden ser nulas en JDK 28. Aunque el concepto “valor” suene parecido a un primitivo, el modelo actual mantiene a las clases de valor como tipos de referencia, y la no nulidad queda para un JEP separado y posterior.

Por qué puede mejorar el rendimiento

El beneficio técnico de Valhalla descansa en dos mecanismos principales mencionados por JEP 401: scalarización y aplanamiento. Ambos apuntan a reducir asignaciones, recortar trabajo del recolector y mejorar densidad de memoria.

La scalarización permite que el JIT descomponga una referencia a objeto de valor en sus campos esenciales. En vez de mover un puntero a una instancia, puede mover directamente sus componentes, como si la estructura se hubiera convertido en registros o variables planas.

Eso recuerda al análisis de escape, pero con un alcance potencialmente más amplio y predecible. La ventaja es que no depende tanto de que el compilador pueda seguir rutas muy específicas de optimización, aunque sigue teniendo límites cuando intervienen tipos más generales como Object o parámetros genéricos borrados.

El aplanamiento, por su parte, permite escribir la representación compacta del valor directamente en un campo o en una celda de arreglo. Ahí aparece la mejora más fácil de visualizar, porque se eliminan punteros intermedios y los datos pueden quedar uno al lado del otro en memoria.

JVM Weekly remarca, no obstante, que ese beneficio no es absoluto en cualquier caso. El tamaño del valor y las garantías de atomicidad importan, y en plataformas actuales una codificación pequeña, incluso dentro de 64 bits con indicador de nulo, tiene muchas más probabilidades de aplanarse bien.

Por eso el rendimiento pleno también depende de piezas que todavía no llegan con JDK 28. Tipos restringidos por nulo y futuras codificaciones de 128 bits serán claves para aplanar clases de valor más grandes sin sacrificar consistencia bajo acceso concurrente.

El caso de Integer, los arreglos y el viejo costo del boxing

Uno de los efectos más llamativos de la vista previa es la migración de clases “basadas en valor” del propio JDK. Entre ellas aparecen los envoltorios de primitivas como Integer, Long y Double, que bajo vista previa pasan a comportarse como clases de valor.

Ese movimiento reduce drásticamente el costo histórico del boxing. Si la caja pierde identidad, la JVM gana margen para scalarizarla y aplanarla, lo que acerca estructuras como Integer[] a una eficiencia más próxima a la de int[].

El ejemplo más claro, sin embargo, sigue siendo el de un arreglo de objetos simples como Point. Antes de Valhalla, un Point[] almacenaba referencias a objetos dispersos en heap, cada uno con encabezado y costos de acceso adicionales.

Con una clase de valor, ese mismo arreglo puede convertirse en un bloque denso donde los puntos se almacenan de forma contigua. El procesador entonces recorre la memoria línea por línea de caché, en lugar de saltar entre punteros y arriesgar fallos costosos.

La ganancia no es solo de velocidad bruta. También permite mantener la abstracción, porque el desarrollador sigue trabajando con una clase llamada Point, con constructor, invariantes y métodos, en vez de romper la lógica en arreglos paralelos de enteros o en estructuras manuales de menor claridad.

Lo que todavía no está listo

El anuncio de JDK 28 no significa que Valhalla haya llegado completo. De hecho, la propia comunicación técnica insiste en que esta integración representa apenas el primer tramo visible de una hoja de ruta bastante más amplia.

La ausencia más importante para muchos equipos será la de los genéricos especializados. Debido al borrado de tipos que define a los genéricos de Java desde 2004, un valor almacenado en una colección como ArrayList<Point> todavía puede terminar materializado como objeto convencional en heap.

Eso significa que la densidad ganada en un Point[] no se traslada automáticamente a colecciones genéricas. Primero deben llegar los llamados genéricos universales a nivel de lenguaje, y después la especialización real a nivel de JVM con diseños heterogéneos para tipos concretos.

Tampoco forman parte de JDK 28 los tipos no nulos para clases de valor. Ese componente es crucial porque no solo simplifica semántica, también puede abrir más espacio para aplanamientos de mayor tamaño al evitar el costo de transportar información de nulidad.

JEP 402, centrado en la mejora del boxing de primitivas, acompaña a JEP 401 pero sigue su propio proceso de maduración. Por eso conviene evitar la idea de que toda la visión de List<int> o de colecciones totalmente planas aterrizará al mismo tiempo.

Qué implica para el ecosistema Java

Para sectores donde la eficiencia de memoria importa de verdad, la señal es relevante desde ya. Finanzas, motores analíticos, procesamiento de imágenes, machine learning, videojuegos y sistemas de alto rendimiento llevan años persiguiendo exactamente este equilibrio entre abstracción y densidad.

En esos entornos, renunciar a objetos para ganar velocidad ha sido una práctica común, aunque incómoda. Valhalla promete recortar ese sacrificio y dar una ruta oficial para mantener modelos de dominio claros sin pagar el costo total del heap tradicional.

El ecosistema también tendrá que revisar supuestos viejos sobre identidad. Código que dependía, de forma explícita o accidental, de comparar wrappers con == o de sincronizar sobre ellos podría exhibir cambios de comportamiento o incluso fallos de compilación bajo la vista previa.

La transición será gradual, y eso parece deliberado. JDK 28 está previsto para marzo de 2027 y no será una versión LTS, de modo que muchas empresas probablemente esperen al siguiente ciclo de soporte extendido antes de adoptar estas capacidades en producción de forma amplia.

Aun así, el valor de la vista previa es inmediato para equipos que construyen bibliotecas, frameworks y runtimes. El período previo al próximo LTS será el momento real para experimentar, detectar incompatibilidades, medir beneficios y aportar retroalimentación antes de una estabilización más amplia.

En ese sentido, la llegada de JEP 401 no es el final del viaje hacia Valhalla, sino el comienzo de la fase en que el diseño se enfrenta al código real del ecosistema. Tras doce años de investigación, descartes y refinamiento, Java ya tiene una primera puerta abierta hacia un modelo de objetos menos rígido y mucho más cercano al hardware moderno.


ADVERTENCIA: DiarioBitcoin ofrece contenido informativo y educativo sobre diversos temas, incluyendo criptomonedas, IA, tecnología y regulaciones. No brindamos asesoramiento financiero. Las inversiones en criptoactivos son de alto riesgo y pueden no ser adecuadas para todos. Investigue, consulte a un experto y verifique la legislación aplicable antes de invertir. Podría perder todo su capital.

Suscríbete a nuestro boletín