Compartir a través de


Motivos para pasar a Java 11 y versiones posteriores

La pregunta no es si debe pasar a Java 11 o una versión posterior, pero cuando. En los próximos años, Ya no se admitirá Java 8 y los usuarios tendrán que pasar a Java 11 o posterior. Argumentamos que hay ventajas para pasar a Java 11 y animar a los equipos a hacerlo lo antes posible.

Desde Java 8, se han agregado nuevas características y se han realizado mejoras. Hay adiciones y modificaciones notables a la API y hay mejoras que mejoran el uso de la memoria, el rendimiento y el inicio.

Transición a Java 11

La transición a Java 11 se puede realizar de forma paso a paso. No es necesario que el código use módulos de Java para ejecutarse en Java 11. Java 11 se puede usar para ejecutar código desarrollado y compilado con JDK 8. Pero hay algunos problemas potenciales, principalmente relacionados con la API en desuso, los cargadores de clases y la reflexión.

El grupo de ingeniería de Java de Microsoft tiene una guía para realizar la transición de Java 8 a Java 11. La Guía de migración de Oracle JDK 9 de Java Platform, Standard Edition y el estado del sistema de módulos: Compatibilidad y migración son otras guías útiles.

Cambios de alto nivel entre Java 8 y 11

Esta sección no enumera todos los cambios realizados en las versiones 9 [1], 10 [2] y 11 [3]. Se resaltan los cambios que tienen un impacto en el rendimiento, el diagnóstico y la productividad.

Módulos [4]

Los módulos abordan los problemas de configuración y encapsulación que son difíciles de administrar en aplicaciones a gran escala que se ejecutan en la ruta de clase. Un módulo es una colección autodescripta de clases e interfaces de Java y recursos relacionados.

Los módulos permiten personalizar las configuraciones en tiempo de ejecución que contienen solo los componentes necesarios para una aplicación. Esta personalización crea una superficie más pequeña y permite que una aplicación esté vinculada estáticamente, mediante jlink, en un entorno de ejecución personalizado para la implementación. Esta superficie más pequeña puede ser especialmente útil en una arquitectura de microservicios.

Internamente, JVM puede aprovechar las ventajas de los módulos de una manera que hace que la carga de clases sea más eficaz. El resultado es un entorno de ejecución que es más pequeño, más ligero y más rápido de iniciarse. Las técnicas de optimización usadas por JVM para mejorar el rendimiento de la aplicación pueden ser más eficaces porque los módulos codifican qué componentes requiere una clase.

En el caso de los programadores, los módulos ayudan a aplicar una encapsulación fuerte al requerir una declaración explícita de los paquetes que exporta un módulo y qué componentes requiere, y al restringir el acceso reflectante. Este nivel de encapsulación hace que una aplicación sea más segura y fácil de mantener.

Una aplicación puede seguir usando la ruta de clase y no tiene que realizar la transición a módulos como requisito para ejecutarse en Java 11.

Generación de perfiles y diagnósticos

Java Flight Recorder [5]

Java Flight Recorder (JFR) recopila datos de diagnóstico y generación de perfiles de una aplicación Java en ejecución. JFR tiene poco impacto en una aplicación Java en ejecución. A continuación, los datos recopilados se pueden analizar con Java Mission Control (JMC) y otras herramientas. Mientras que JFR y JMC eran características comerciales en Java 8, ambas son de código abierto en Java 11.

Java Mission Control [6]

Java Mission Control (JMC) proporciona una visualización gráfica de los datos recopilados por Java Flight Recorder (JFR) y es de código abierto en Java 11. Además de la información general sobre la aplicación en ejecución, JMC permite al usuario explorar en profundidad los datos. JFR y JMC se pueden usar para diagnosticar problemas en tiempo de ejecución, como pérdidas de memoria, sobrecarga de GC, métodos activos, cuellos de botella de subprocesos y E/S de bloqueo.

Registro unificado [7]

Java 11 tiene un sistema de registro común para todos los componentes de la JVM. Este sistema de registro unificado permite al usuario definir qué componentes registrar y a qué nivel. Este registro específico es útil para realizar análisis de causa principal en bloqueos de JVM y para diagnosticar problemas de rendimiento en un entorno de producción.

Generación de perfiles de montón de baja sobrecarga [8]

Se ha agregado un nuevo API a la Interfaz de Herramientas de Máquina Virtual de Java (JVMTI) para realizar el muestreo de asignaciones de heap de Java. El muestreo tiene una sobrecarga baja y se puede habilitar continuamente. Aunque la asignación del montón se puede supervisar con Java Flight Recorder (JFR), el método de muestreo de JFR solo funciona en las asignaciones. La implementación de JFR también puede perder asignaciones. Por el contrario, el muestreo del montón en Java 11 puede proporcionar información sobre objetos activos y inactivos.

Los proveedores de supervisión del rendimiento de aplicaciones (APM) empiezan a usar esta nueva característica y el grupo de ingeniería de Java está investigando su posible uso con las herramientas de supervisión del rendimiento de Azure.

StackWalker [9]

La obtención de una instantánea de la pila para el hilo actual se usa a menudo al registrar eventos. El problema es la cantidad de seguimiento de la pila que se va a registrar y si se va a registrar el seguimiento de la pila en absoluto. Por ejemplo, es posible que quiera ver el seguimiento de la pila solo para una excepción determinada de un método. La clase StackWalker (agregada en Java 9) ofrece una instantánea de la pila y proporciona métodos que otorgan al programador un control detallado sobre cómo consumir el seguimiento de la pila.

Recolección de basura [10]

Los siguientes recolectores de basura están disponibles en Java 11: Serial, Parallel, Garbage-First y Epsilon. El recolector de basura predeterminado en Java 11 es el Garbage First Garbage Collector (G1GC).

Se mencionan tres coleccionistas más para completar la información. El Z Garbage Collector (ZGC) es un recolector simultáneo de baja latencia que intenta mantener los tiempos de pausa por debajo de los 10 ms. ZGC está disponible como una característica experimental en Java 11. El recolector Shenandoah es un recolector de baja pausa que reduce los tiempos de pausa de recolección de basura (GC) al realizar más recolección de basura concurrentemente con el programa Java en ejecución. Shenandoah es una característica experimental en Java 12, pero hay backports a Java 11. El colector de barrido y marcado concurrente (CMS) está disponible, pero ha quedado en desuso desde Java 9.

JVM establece los valores predeterminados de GC para el caso de uso promedio. A menudo, estos valores predeterminados y otras configuraciones de GC deben optimizarse para optimizar el rendimiento o la latencia óptimos, según los requisitos de la aplicación. La optimización correcta del GC requiere un conocimiento profundo de la GC, experiencia que proporciona el grupo de ingeniería de Java de Microsoft .

G1GC

El recolector de elementos no utilizados predeterminado en Java 11 es el recolector de elementos no utilizados G1 (G1GC). El objetivo de G1GC es lograr un equilibrio entre la latencia y el rendimiento. El recolector de basura G1 intenta lograr un alto rendimiento para cumplir con los objetivos de tiempo de pausa con una alta probabilidad. G1GC está diseñado para evitar colecciones completas, pero cuando las colecciones concurrentes no pueden reclamar memoria lo suficientemente rápidamente, se producirá una GC completa de reserva. El GC completo utiliza el mismo número de hilos de trabajo paralelos que las generaciones jóvenes y mixtas.

GC paralelo

El recopilador paralelo es el recopilador predeterminado en Java 8. Parallel GC es un recolector de rendimiento que usa varios subprocesos para acelerar la recolección de basura.

Epsilon [11]

El recolector de basura Epsilon maneja las asignaciones, pero no reclama ninguna memoria. Cuando el montón se agote, la JVM se apagará. Epsilon es útil para los servicios de corta duración y para las aplicaciones conocidas por no generar basura.

Mejoras para contenedores de Docker [12]

Antes de Java 10, la JVM no reconoceba las restricciones de memoria y CPU establecidas en un contenedor. En Java 8, por ejemplo, la JVM establecerá por defecto el tamaño máximo del montón a ¼ de la memoria física del host subyacente. A partir de Java 10, JVM usa restricciones establecidas por grupos de control de contenedor (cgroups) para establecer límites de memoria y CPU (consulte la nota siguiente). Por ejemplo, el tamaño máximo predeterminado del montón es 1/4 del límite de memoria del contenedor (por ejemplo, 500 MB para -m2G).

Las opciones de JVM también se agregaron para proporcionar a los usuarios de contenedores de Docker un control específico sobre la cantidad de memoria del sistema que se usará para el montón de Java.

Esta compatibilidad está habilitada de forma predeterminada y solo está disponible en plataformas basadas en Linux.

Nota:

La mayor parte del trabajo de habilitación de cgroup se retroportó a Java 8 a partir de la versión jdk8u191. Es posible que las mejoras adicionales no se retroporten necesariamente a 8.

Archivos JAR de varias versiones [13]

En Java 11 es posible crear un archivo "jar" que contenga múltiples versiones específicas para la versión de Java de los archivos de clase. Los archivos JAR de varias versiones permiten a los desarrolladores de bibliotecas admitir varias versiones de Java sin tener que enviar varias versiones de archivos jar. Para el consumidor de estas bibliotecas, los archivos JAR de varias versiones resuelven el problema de tener que hacer coincidir archivos jar específicos con destinos en tiempo de ejecución específicos.

Mejoras de rendimiento varias

Los siguientes cambios en JVM tienen un impacto directo en el rendimiento.

  • JEP 197: caché de código segmentado [14] : divide la caché de código en segmentos distintos. Esta segmentación proporciona un mejor control de la superficie de memoria de JVM, acorta el tiempo de examen de los métodos compilados, reduce significativamente la fragmentación de la caché de código y mejora el rendimiento.

  • JEP 254: Cadenas compactas [15] : cambia la representación interna de una cadena de dos bytes por carácter a uno o dos bytes por carácter, según la codificación char. Dado que la mayoría de las cadenas contienen caracteres ISO-8859-1/Latin-1, este cambio reduce eficazmente la cantidad de espacio necesario para almacenar una cadena.

  • JEP 310: Application Class-Data Sharing [16] - Class-Data Sharing reduce el tiempo de inicio al permitir que las clases archivadas sean mapeadas en memoria durante el tiempo de ejecución. Application Class-Data Sharing amplía el uso compartido de datos de clase al permitir que las clases de aplicación se coloquen en el archivo CDS. Cuando varios JVM comparten el mismo archivo de archivo, se guarda la memoria y el tiempo de respuesta general del sistema mejora.

  • JEP 312: Thread-Local protocolos de enlace [17] : permite ejecutar una devolución de llamada en subprocesos sin realizar un punto seguro de máquina virtual global, lo que ayuda a la máquina virtual a lograr una menor latencia al reducir el número de puntos de seguridad globales.

  • Asignación diferida de subprocesos del compilador [18] : en modo de compilación en capas, la máquina virtual inicia un gran número de subprocesos del compilador. Este modo es el valor predeterminado en los sistemas con muchas CPU. Estos subprocesos se crean independientemente de la memoria disponible o del número de solicitudes de compilación. Los subprocesos consumen memoria incluso cuando están inactivos (la mayor parte del tiempo), lo que conduce a un uso ineficiente de los recursos. Para solucionar este problema, la implementación se ha cambiado para iniciar solo un subproceso del compilador de cada tipo durante el inicio. Iniciar subprocesos adicionales y apagar subprocesos sin usar se controla dinámicamente.

Los siguientes cambios en las bibliotecas principales tienen un impacto en el rendimiento del código nuevo o modificado.

  • JEP 193: Identificadores de variables [19] : define un medio estándar para invocar los equivalentes de varias operaciones java.util.concurrent.atomic y sun.misc.Unsafe en campos de objeto y elementos de matriz, un conjunto estándar de operaciones de barrera para el control específico de la ordenación de memoria y una operación estándar de barrera de alcance para asegurarse de que un objeto al que se hace referencia sigue siendo fuertemente accesible.

  • JEP 269: Métodos de fábrica de conveniencia para colecciones [20] - Define las API de biblioteca para que sea conveniente crear instancias de colecciones y mapas con un pequeño número de elementos. Los métodos de fábrica estáticos en las interfaces de colección que crean instancias de colección compactas y no modificables. Estas instancias son intrínsecamente más eficaces. Las API crean colecciones que se representan de forma compacta y no tienen una clase contenedora.

  • JEP 285: Sugerencias de Espera Activa [21] : proporciona una API que permite a Java sugerir al sistema en tiempo de ejecución que se encuentra en un bucle de espera activa. Algunas plataformas de hardware se benefician de la notificación por software de que un subproceso está en un estado de espera activa.

  • JEP 321: Cliente HTTP (estándar) [22]- Proporciona una nueva API de cliente HTTP que implementa HTTP/2 y WebSocket y puede reemplazar la API httpURLConnection heredada.

Referencias

[1] Oracle Corporation, "Notas de la versión del Kit de desarrollo de Java 9" (en línea). Disponible: https://www.oracle.com/technetwork/java/javase/9u-relnotes-3704429.html. (Consultado el 13 de noviembre de 2019).

[2] Oracle Corporation, "Java Development Kit 10 Release Notes" (En línea). Disponible: https://www.oracle.com/technetwork/java/javase/10u-relnotes-4108739.html. (Consultado el 13 de noviembre de 2019).

[3] Oracle Corporation, "Notas de la versión del Kit de desarrollo de Java 11" (En línea). Disponible: https://www.oracle.com/technetwork/java/javase/11u-relnotes-5093844.html. (Consultado el 13 de noviembre de 2019).

[4] Oracle Corporation, "Project Jigsaw", 22 de septiembre de 2017. (En línea). Disponible: http://openjdk.java.net/projects/jigsaw/. (Consultado el 13 de noviembre de 2019).

[5] Oracle Corporation, "JEP 328: Flight Recorder", 9 de septiembre de 2018. (En línea). Disponible: http://openjdk.java.net/jeps/328. (Consultado el 13 de noviembre de 2019).

[6] Oracle Corporation, "Mission Control", 25 de abril de 2019. (En línea). Disponible: https://wiki.openjdk.java.net/display/jmc/Main. (Consultado el 13 de noviembre de 2019).

[7] Oracle Corporation, "JEP 158: Unified JVM Logging", 14 de febrero de 2019. (En línea). Disponible: http://openjdk.java.net/jeps/158. (Consultado el 13 de noviembre de 2019).

[8] Oracle Corporation, "JEP 331: Generación de perfiles de montón de bajo costo", 5 de septiembre de 2018. (En línea). Disponible: http://openjdk.java.net/jeps/331. (Consultado el 13 de noviembre de 2019).

[9] Oracle Corporation, "JEP 259: Stack-Walking API", 18 de julio de 2017. (En línea). Disponible: http://openjdk.java.net/jeps/259. (Consultado el 13 de noviembre de 2019).

[10] Oracle Corporation, "JEP 248: Make G1 the Default Garbage Collector", 12 de septiembre de 2017. (En línea). Disponible: http://openjdk.java.net/jeps/248. (Consultado el 13 de noviembre de 2019).

[11] Oracle Corporation, "JEP 318: Epsilon: Un recolector de basura sin operación", 24 de septiembre de 2018. (En línea). Disponible: http://openjdk.java.net/jeps/318. (Consultado el 13 de noviembre de 2019).

[12] Oracle Corporation, "JDK-8146115: Mejorar la detección de contenedores de Docker y el uso de la configuración de recursos", el 16 de septiembre de 2019. (En línea). Disponible: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8146115. (Consultado el 13 de noviembre de 2019).

[13] Oracle Corporation, "JEP 238: Archivos JAR de versión múltiple", 22 de junio de 2017. (En línea). Disponible: http://openjdk.java.net/jeps/238. (Consultado el 13 de noviembre de 2019).

[14] Oracle Corporation, "JEP 197: Segmented Code Cache", 28 de abril de 2017. (En línea). Disponible: http://openjdk.java.net/jeps/197. (Consultado el 13 de noviembre de 2019).

[15] Oracle Corporation, "JEP 254: Compact Strings", 18 de mayo de 2019. (En línea). Disponible: http://openjdk.java.net/jeps/254. (Consultado el 13 de noviembre de 2019).

[16] Oracle Corporation, "JEP 310: Application Class-Data Sharing", 17 de agosto de 2018. (En línea). Disponible: https://openjdk.java.net/jeps/310. (Consultado el 13 de noviembre de 2019).

[17] Oracle Corporation, "JEP 312: Thread-Local protocolos de enlace", 21 de agosto de 2019. (En línea). Disponible: https://openjdk.java.net/jeps/312. (Consultado el 13 de noviembre de 2019).

[18] Oracle Corporation, "JDK-8198756: Asignación diferida de subprocesos del compilador", 29 de octubre de 2018. (En línea). Disponible: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8198756. (Consultado el 13 de noviembre de 2019).

[19] Oracle Corporation, "JEP 193: Controladores de variables", 17 de agosto de 2017. (En línea). Disponible: https://openjdk.java.net/jeps/193. (Consultado el 13 de noviembre de 2019).

[20] Oracle Corporation, "JEP 269: Convenience Factory Methods for Collections", 26 de junio de 2017. (En línea). Disponible: https://openjdk.java.net/jeps/269. (Consultado el 13 de noviembre de 2019).

[21] Oracle Corporation, "JEP 285: Spin-Wait Hints", 20 de agosto de 2017. (En línea). Disponible: https://openjdk.java.net/jeps/285. (Consultado el 13 de noviembre de 2019).

[22] Oracle Corporation, "JEP 321: Cliente HTTP (estándar)," 27 de septiembre de 2018. (En línea). Disponible: https://openjdk.java.net/jeps/321. (Consultado el 13 de noviembre de 2019).