¿Tu código Java está realmente optimizado para la nube? Descubre cómo las últimas actualizaciones de la plataforma transforman el rendimiento de tus aplicaciones sin cambiar una sola línea de sintaxis.

Como cada 6 meses, ya tenemos aquí una nueva versión de la plataforma Java: Java 26.
Aparentemente esta versión no trae nada relevante desde el punto de vista de los desarrolladores. Pero si te paras a ver con detalle las "tripas" de los JEPs de esta nueva versión (no LTS, por cierto) descubres que, en realidad, Java 26 es una actualización interesante de refinamiento de la plataforma.
Esta versión no busca fuegos artificiales sintácticos, sino robustez en tres frentes:
- Conectividad moderna con HTTP/3
- Un rendimiento del recolector G1 optimizado para la alta concurrencia
- Un blindaje definitivo a la integridad del lenguaje.
Estos cambios "bajo el capó" pueden tener un impacto mucho mayor en producción que cualquier nueva palabra reservada.
1.- ¿Por qué HTTP/3 en el cliente nativo de Java cambia las reglas del juego?
La JEP 517 añade el soporte nativo para HTTP/3 en la HttpClient API.
HTTP/3 Es la versión más reciente del protocolo HTTP utilizado para la comunicación en la web El cambio trascendental aquí es el protocolo de transporte: pasamos de TCP a QUIC, que es un protocolo diseñado para ser más eficiente que los protocolos tradicionales. Mientras que las versiones anteriores (HTTP/1.1 y HTTP/2) utilizan TCP como base, HTTP/3 utiliza QUIC como su protocolo de transporte subyacente . Esta transición permite superar limitaciones históricas de TCP en la red moderna, como por ejemplo:
- Eliminación del bloqueo de cabecera (Head-of-Line Blocking): a diferencia de sus predecesores, HTTP/3 evita que el retraso de un solo paquete detenga toda la transmisión de datos, lo que mejora la fluidez de la conexión.
- Conexiones más rápidas: ofrece la posibilidad de realizar handshakes (saludos de conexión) más rápidos, reduciendo la latencia inicial al establecer comunicación con un servidor.
- Fiabilidad en redes inestables: proporciona un transporte más confiable, especialmente en entornos con alta pérdida de paquetes, como redes móviles o conexiones de baja calidad.
Actualmente, HTTP/3 ya está soportado por la práctica totalidad de los navegadores, y está desplegado en aproximadamente un tercio de todos los sitios web, y subiendo.
Su inclusión en Java es fundamental para el desarrollo de aplicaciones modernas y cubre una deuda que hace tiempo que tenía, ya que sí que está disponible en muchas otras plataformas.
En entornos cloud-native, donde la latencia de red y la pérdida de paquetes son algo que puede darse a menudo, el paso a QUIC elimina el Head-of-Line Blocking (HOLB).
Configuración del cliente HTTP/3 con Java
La implementación en Java permite a los desarrolladores elegir HTTP/3 de forma opcional (opt-in), contando con mecanismos transparentes para volver a HTTP/2 o HTTP/1.1 si el servidor de destino no soporta la versión más reciente.
Implica tan solo una línea:
var client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
// O mediante opciones de descubrimiento en el request
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.internal.service"))
.header("H3_DISCOVERY", "true")
.build();
2.- ¿Cómo consigue el recolector de basura G1 de Java mejorar el rendimiento sin que te des cuenta?
El recolector por defecto, G1 GC, recibe en la JEP 522 una de sus mejores optimizaciones: la doble tabla de tarjetas (double card table).
Anteriormente, los hilos de la aplicación y los del recolector de basura (GC: Garbage Collector) competían por sincronizarse sobre una única tabla de tarjetas para registrar modificaciones de objetos. Con el nuevo diseño, se eliminan los puntos de contención: los hilos de la aplicación escriben en una tabla sin bloqueos, mientras el GC procesa la otra.
Gracias a esto se consigue:
- Eficiencia de CPU: las barreras de escritura (write barriers) se han reducido de 50 a solo 12 instrucciones en arquitecturas x64.
- Rendimiento: mejora del 5-15% en el throughput general, especialmente en aplicaciones con muchas actualizaciones de referencias.
- Memoria: el coste es mínimo, apenas 2 MB por cada 1 GB en el montón (heap), una inversión muy pequeña frente a la ganancia en latencia.
Pero no, no todo es la JEP 522.
Un problema que se da en ocasiones es el llamado "GC thrashing". Se refiere a una situación crítica en la que el recolector de basura dedica una cantidad excesiva de tiempo a intentar liberar memoria, pero logra recuperar muy poco espacio en el heap de Java . En este escenario, la aplicación apenas puede ejecutarse porque la mayor parte de los recursos de la CPU están siendo consumidos por los ciclos constantes de recolección de basura.
Java 26 añade otras dos mejoras vitales para evitar ese temido "GC thrashing":
- Reclamación de Objetos "Humongous" (enormes) (JDK-8048180): este es un problema conocido hace tiempo que te explica muy bien (curiosamente) Microsoft aquí: What’s the deal with humongous objects in Java?. Ahora el recolector G1 puede reclamar sin problema objetos de gran tamaño (que ocupan más de media región) incluso si contienen referencias. Antes, esto solo ocurría en recolecciones de basura completos.
- Límite de Sobrecarga (JDK-8212084): al igual que GC paralelo, G1 ahora lanzará una excepción
OutOfMemoryError de forma preventiva si detecta que está pasando más del 98% del tiempo recolectando para liberar menos del 2% del heap durante cinco ciclos consecutivos.
3.- ¿Por qué 'final' empezará a significar de verdad 'final'?
Durante décadas, el modificador final fue una sugerencia de la que la reflexión podía pasar completamente mediante setAccessible(true). La JEP 500 inicia el fin de esta era para por fin proteger la integridad del tiempo de ejecución.
En Java 26, intentar mutar un campo final mediante reflexión generará ahora un warning por defecto, pero el objetivo es bloquearlo totalmente en próximas versiones.
Esto no es solo por seguridad: es una capacidad para el compilador JIT (Just In time: convierte en tiempo de ejecución bytecode JVM en código máquina nativo para mejorar el rendimiento), que ahora puede aplicar constant folding (evaluar expresiones constantes en compilación) de forma mucho más agresiva y segura.
Se puede controlar este comportamiento con un parámetro para el compilador: --illegal-final-field-mutation que permite valores como allow, warn (que es el valor por defecto), debug (con traza de la pila) y deny.
Recomendación: las bibliotecas de serialización deben migrar al uso de sun.reflect.ReflectionFactory, que proporciona un mecanismo soportado para instanciar objetos con campos final sin romper la integridad del lenguaje.
4.- Joyas ocultas y mejoras GA (General Availability)
Java 26 está plagado de otros detalles de calidad de vida que impactan directamente en la eficiencia a la hora de ejecutar aplicaciones:
- Porcentaje inicial de RAM a 0 (JDK-8371986): un cambio espectacular a la hora de trabajar en Docker y Kubernetes. Al eliminar el valor por defecto de 1.5625%, se permite que la JVM ajuste el heap inicial, evitando desperdicio de recursos en contenedores con mucha memoria asignada.
- UUIDv7: soporte nativo para este tipo de identificadores universales mediante
UUID.ofEpochMillis(long). Estos UUIDs son ordenables cronológicamente, lo que los hace perfectos como claves primarias para mejorar el rendimiento de índices en bases de datos. .NET hace tiempo que los tiene disponibles y esta es una deuda que por fin cubre Java también.
- AOT Object Caching (JEP 516): ahora compatible con ZGC (el recolector de basura de baja latencia de JVM HotSpot). La clave técnica es el paso de direcciones de memoria físicas a índices lógicos en la caché, permitiendo que el arranque rápido que proporciona Project Leyden sea independiente del recolector de basura utilizado.
- C2 JIT Potenciado: el compilador ahora puede optimizar métodos con más de 30 parámetros, eliminando el bailout que obligaba a caer en el intérprete o en C1 en métodos excesivamente complejos.
- JDBC 4.5 y Process API:
Process ahora es AutoCloseable (compatible con try-with-resources). JDBC 4.5 añade soporte para tipos SQL como DECFLOAT y JSON.
5.- Estado de las funciones en preview y su evolución
Java 26 mantiene la incubación de tecnologías que están a las puertas de cambiar nuestra forma de programar, pero que requieren prudencia y de las que ya te hemos hablado hace poco.
| Característica |
Estado actual |
Cambio clave en Java 26 |
| Lazy Constants (JEP 526) |
2ª Preview |
Renombradas (antes eran Stable Values). Enfoque en APIs de alto nivel, se eliminan métodos de bajo nivel como setOrThrow. |
| Structured Concurrency (JEP 525) |
6ª Preview |
Nuevo método onTimeout en el Joiner. allSuccessfulOrThrow ahora devuelve una List en lugar de un Stream. |
| Vector API (JEP 529) |
11ª Incubación |
Bloqueada por Proyecto Valhalla. No saldrá de incubación hasta que los Value Classes permitan una representación en memoria eficiente. |
| Primitive Types in Patterns (JEP 530) |
4ª Preview |
Definición más estricta de dominancia en switch para detectar errores de lógica en tiempo de compilación. |
| Codificaciones PEM (JEP 524) |
2ª Preview |
Refinamientos en PEMEncoder y soporte para cifrado en KeyPair. |
Conclusión: Mirando hacia el horizonte de Java
Java 26 es una versión pragmática. La llegada de HTTP/3 nativo y la optimización muy enfocada del G1 GC demuestran que el foco actual de Oracle y la comunidad OpenJDK es la eficiencia en la nube.
Aunque no sea una LTS (versión con soporte a largo plazo) con Java 26 tenemos una JVM que no solo ejecuta nuestro código, sino que lo protege y lo acelera de formas que antes requerían configuraciones manuales complejas.