
Han pasado seis meses, lo que significa que es hora de instalar Java nuevo . Fue un largo viaje, y pocos llegaron al final. Las líneas sin procesar cayeron de JEP interesantes, pero hablaremos del resto bajo el corte.
Como va todo
El lanzamiento de la nueva versión de Java se realiza de acuerdo con el nuevo ciclo de lanzamiento "acelerado" con una duración de aproximadamente seis meses. Las fechas exactas se definen en la página del proyecto . Hubo varias fases principales para JDK 12:
- 13/12/2018 - La primera fase de la desaceleración (en este momento, se hace una bifurcación desde la rama principal en el repositorio);
- 2019/01/17 - La segunda fase de la desaceleración (completa todo lo que sea posible);
- 2019/02/07 - Release candidato (solo se corrigen los errores más importantes);
- 2019/03/19 - Lanzamiento, Disponibilidad general. <- estás aquí
¿Qué tenemos de este horario? Sí, de hecho, nada: acabamos de llegar a la línea de meta y observamos a los amantes del legado desde una altura del nuevo JDK 12 nuevo.
Bichos! Pánico! Todo al fondo!

Cuando sale una nueva versión que no es LTS, generalmente a todos no les importan las nuevas características. Es más interesante si todo se vendrá abajo al infierno.
Por supuesto, hay muchos errores, pero no en JDK 12 :) A juzgar por el jir, todo es normal:

Citaré la solicitud para que comprenda exactamente cuál es la "norma":
project = JDK AND issuetype = Bug AND status in (Open, "In Progress", New) AND priority in (P1) AND (fixVersion in (12) OR fixVersion is EMPTY AND affectedVersion in (12) AND affectedVersion not in regexVersion("11.*", "10.*", "9.*", "8.*", "7.*", "6.*")) AND (labels is EMPTY OR labels not in (jdk12-defer-request, noreg-demo, noreg-doc, noreg-self)) AND (component not in (docs, globalization, infrastructure) OR component = infrastructure AND subcomponent = build) AND reporter != "Shadow Bug" ORDER BY priority, component, subcomponent, assignee
Por supuesto, en general los errores tienen un lugar para estar, no irán a ningún lado en un proyecto tan grande. Solo se afirma que en este momento no se han notado los errores P1.
Una comunicación más formal con los errores se declara en un documento especial, JEP 3: Proceso de lanzamiento de JDK , que es propiedad de nuestro administrador inmortal en las turbulentas olas del Océano Java: Mark Reinhold.
Y en particular, vale la pena desenterrar un párrafo que diga quién tiene la culpa y qué hacer cómo transferir boletos si no tienes tiempo para el 12 ° lanzamiento. Es necesario poner en el jdk$N-defer-request
errores la etiqueta jdk$N-defer-request
en la que N indica desde qué versión desea transferir y dejar un comentario, cuya primera línea es Solicitud de aplazamiento . Además, la revisión de todas estas solicitudes es realizada por los líderes de las áreas y proyectos respectivos.
Los problemas de pasar TCK no se pueden ignorar de esta manera: se garantiza que Java sigue siendo Java, y no algo parecido a una rana. La jdk$N-defer-request label
nunca desaparece. Es interesante lo que hacen con las personas que violan la regla de no eliminar etiquetas: sugiero alimentar a los conejillos de indias.
Sin embargo, de esta manera puede ver cuántos errores se han portado a JDK 13. Probemos con esta consulta:
project = JDK AND issuetype = Bug AND status in (Open, "In Progress", New) AND (labels in (jdk12-defer-request) AND labels not in (noreg-demo, noreg-doc, noreg-self)) AND (component not in (docs, globalization, infrastructure) OR component = infrastructure AND subcomponent = build) AND reporter != "Shadow Bug" ORDER BY priority, component, subcomponent, assignee
Solo 1 pieza, JDK-8216039 : "TLS con BC y RSASSA-PSS rompe ECDHServerKeyExchange". No es grueso. Si este argumento aún no ayuda, entonces, como su abogado, le recomiendo probar un sedante.
¿Y cuál es el resultado final?

Está claro que la mayoría de las características no afectan a los usuarios (programadores de Java), sino a los desarrolladores de OpenJDK. Por lo tanto, por si acaso, divido las características en externas e internas . Puede omitir los internos, pero estoy ofendido, escribí mucho texto.
189: Shenandoah: un recolector de basura de bajo tiempo de pausa (experimental)
Característica externa En resumen, a la gente no le gusta cuando Java se ralentiza, especialmente si el SLA requiere una capacidad de respuesta del orden de 10-500 milisegundos. Ahora tenemos un GC libre de bajo impacto que está tratando de trabajar más cerca del borde izquierdo de este rango. La compensación es que intercambiamos la CPU y la RAM para reducir la latencia. Las fases de etiquetado y compactación de la cadera funcionan en paralelo con hilos de aplicaciones en vivo. Las pequeñas pausas restantes se deben al hecho de que aún necesita buscar y actualizar las raíces del gráfico de objetos.
Si nada de lo anterior tiene sentido para usted, no importa, Shenandoah simplemente funciona , independientemente de comprender o no comprender los procesos subyacentes.
Alexei Shipilev, Christina Flood y Roman Kennke están trabajando en ello: debe esforzarse mucho para no saber acerca de estas personas. Si generalmente comprende cómo funciona GC pero no se imagina lo que un desarrollador puede hacer allí, le recomiendo que lea la maravillosa traducción del maravilloso artículo de Leshina "Recolector de basura casero para OpenJDK" o la serie JVM Anatomy Quarks . Esto es muy interesante
Extremadamente importante Oracle decidió no enviar a Sheandoah con ninguna de sus versiones de lanzamiento, ni la de jdk.java.net ni la de oracle.com. Dado que Shenandoah es una de las características más importantes de JDK 12, vale la pena instalar alguna otra asamblea oficial, por ejemplo, de Azul .
230: Suite Microbenchmark
Característica interior Si alguna vez trató de escribir microbenchmarks, entonces sabe que esto se hace en JMH. JMH es un marco para crear, ensamblar, lanzar y analizar microbenchmarks para Java y otros lenguajes JVM, usted mismo comprende quién lo escribió (todas las coincidencias son aleatorias). Desafortunadamente, no todo lo que se hace en el mundo de las aplicaciones "normales" se puede aplicar dentro del JDK. Por ejemplo, es poco probable que veamos el código normal de Spring Framework allí.
Afortunadamente, a partir de la versión 12, puede usar al menos JMH, y ya hay un conjunto de pruebas escritas en él. Puede verlo en jdk/jdk/test/micro/org/openjdk/bench
(puede mirar directamente en el navegador, esta ruta es un enlace).
Por ejemplo, así es como se ve la prueba de GC .
Permítame recordarle que no tenemos StackOverflow aquí, y está prohibido usar código de copiar y pegar, aquí y en lo sucesivo, sin leer y observar todas las licencias del archivo correspondiente y del proyecto OpenJDK en general, de lo contrario, obtendrá fácilmente los últimos calcetines para demandar.
@BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @State(Scope.Thread) public class Alloc { public static final int LENGTH = 400; public static final int ARR_LEN = 100; public int largeLen = 100; public int smalllen = 6; @Benchmark public void testLargeConstArray(Blackhole bh) throws Exception { int localArrlen = ARR_LEN; for (int i = 0; i < LENGTH; i++) { Object[] tmp = new Object[localArrlen]; bh.consume(tmp); } }
325: Cambiar expresiones (vista previa)
Característica externa Cambiará fundamentalmente su enfoque para escribir interruptores sin fin con una longitud de más de dos pantallas. Mira
Virgin Java Switch vs ...
int dayNum = -1; switch (day) { case MONDAY: case FRIDAY: case SUNDAY: dayNum = 6; break; case TUESDAY: dayNum = 7; break; case THURSDAY: case SATURDAY: dayNum = 8; break; case WEDNESDAY: dayNum = 9; break; }
Por qué es malo : hay muchas letras, puedes saltarte el descanso (especialmente si eres adicto a las drogas o estás enfermo de TDAH).
... vs Chad Java Swtich Expression!
int dayNum = switch (day) { case MONDAY -> 0; case TUESDAY -> 1; default -> { int k = day.toString().length(); int result = f(k); break result; } };
Por qué bueno : pocas letras, seguro, conveniente, nueva característica genial.
Bonificación : si eres sádico, te dará la más profunda satisfacción, ya que miles de desarrolladores de IDE ahora están atormentados con el soporte de esta función. Si lany , si? Puede atraparlo después del informe el 6 de abril y pedirle amablemente que revele todos los detalles sucios.
Esta es una función de vista previa, ¡simplemente no funcionará! Al compilar, en javac
debe pasar las opciones de línea de comandos --enable-preview --release 12
, y ejecutar java
- solo el indicador --enable-preview
.
334: API de constantes JVM
Característica interior Los desarrolladores quieren manipular archivos de clase. Debe hacer esto convenientemente, y esta es la declaración del problema. Al menos, eso es lo que Brian Goetz, propietario de este JEP, dijo :-) Todo esto es parte de un campo de batalla más grande, pero por ahora no profundizaremos.
Cada clase de Java tiene un denominado "grupo constante" donde hay un volcado de algunos valores (como cadenas e ints), o entidades de tiempo de ejecución como clases y métodos. Puede profundizar en este volcado utilizando la instrucción ldc - "load costant", por lo que toda esta basura se llama constantes cargables. Todavía hay un caso especial para invocarse dinámicamente, pero no importa.
Si trabajamos con archivos de clase, entonces queremos simular convenientemente instrumentos de código de bytes y, por lo tanto, constantes cargables. El primer deseo es simplemente crear los tipos de Java correspondientes, pero ¿cómo presentarlos con una clase "viva", la estructura CONSTANT_Class_info
? Class
objetos de Class
dependen de la exactitud y consistencia de la carga de clases, y con la carga de clases en Java, se crea una bacanal infernal. Para empezar, no todas las clases se pueden cargar en la máquina virtual, ¡pero aún debe describirlas!
Me gustaría administrar de alguna manera cosas como clases, métodos y bestias menos conocidas como manejadores de métodos y constantes dinámicas, teniendo en cuenta todas estas sutilezas.
Esto se resuelve introduciendo nuevos tipos de enlaces simbólicos basados en valores (en el sentido de JVMS 5.1 ), cada uno de los cuales describe un tipo específico de constante. Describe puramente nominalmente, aislado de las clases de carga o problemas de acceso. Viven en paquetes como java.lang.invoke.constant
y no lo solicitan, pero puede consultar el parche aquí .
340: Un puerto AArch64, no dos
Característica externa Ya en JDK 9 había una situación extraña cuando Oracle y Red Hat pusieron simultáneamente en alerta a sus puertos ARM. Y ahora vemos el final de la historia: la parte de 64 bits del puerto de Oraklov se eliminó de la corriente arriba.
Podría haber profundizado en la historia durante mucho tiempo, pero hay una mejor manera. BellSoft participó en el desarrollo de este JEP, y su oficina está ubicada en San Petersburgo, al lado de la antigua oficina de Oracle.
Por lo tanto, inmediatamente recurrí a Alexey Voitilov, CTO de BellSoft:
"BellSoft lanza Liberica JDK, que, además de x86 Linux / Windows / Mac y Solaris / SPARC, también es compatible con ARM. Comenzando con JDK 9 para ARM, nos centramos en mejorar el rendimiento del puerto AARCH64 para aplicaciones de servidor y seguimos admitiendo el puerto ARM de 32 bits para Entonces, en el momento del lanzamiento de JDK 11, había una situación en la que nadie admitía la parte del puerto de 64 bits de Oracle (incluido Oracle), y la comunidad OpenJDK decidió eliminarla para centrarse en el puerto AARCH64. véase, por ejemplo, PEC 315 , que nos integrado en JDK 11) y, a partir de JDK 12, admite todas las características presentes en el puerto de Oracle (la última, Minimal VM, que integré en septiembre). Por lo tanto, me complace ayudar a Bob Vandette a eliminar este rudimento en JDK 12. Como resultado, OpenJDK la comunidad recibió un puerto en AARCH64 y un puerto ARM32, lo que ciertamente los hace más fáciles de soportar ".
341: Archivos CDS predeterminados
Característica interior El problema es que al comienzo de una aplicación Java, se cargan miles de clases, lo que crea la sensación de que Java se ralentiza significativamente al inicio. Pero quién está allí para mentir, esto no es solo una "sensación", es así. Para solucionar el problema desde la antigüedad, se practican varios rituales.
Class Data Sharing es una característica que nos ha llegado desde tiempos inmemoriales , como una característica comercial de JDK 8 Update 40. Le permite empacar toda esta basura de inicio en un archivo de algún formato propio (no necesita saber cuál), después de lo cual la velocidad de lanzamiento Las aplicaciones están aumentando. Y después de un tiempo, apareció JEP 310 : Application Class-Data Sharing, que nos permitió trabajar de la misma manera no solo con las clases del sistema, sino también con las clases de aplicación.
Para las clases JDK, se ve así. Primero, volcamos las clases con el comando java -Xshare:dump
, y luego ejecutamos la aplicación, diciéndole que use este caché: java -Xshare:on -jar app.jar
. Todo, la startup ha mejorado un poco. ¿Sabías sobre esta característica? ¡Muchos que aún no lo saben!
Parece extraño aquí: ¿por qué cada vez escribe ritualmente -Xshare:dump
si el resultado predeterminado de este comando es un poco predecible incluso en la etapa de creación de la distribución JDK? De acuerdo con la documentación , si la distribución de Java 8 se instaló utilizando el instalador, justo en el momento de la instalación debería ejecutar los comandos necesarios para usted. Al igual, el instalador está minando silenciosamente en la esquina. Pero por que? ¿Y qué hacer con la distribución, que se distribuye no como un instalador, sino como un archivo zip?
Es simple: comenzando con JDK 12, el archivo CDS será generado por los creadores del kit de distribución, inmediatamente después del enlace. Incluso para compilaciones nocturnas (siempre que sean de 64 bits y nativas, no para compilación cruzada).
Los usuarios ni siquiera necesitan saber acerca de la presencia de esta característica, porque, comenzando con JDK 11, -Xshare:auto
habilitado de forma predeterminada, y dicho archivo se recuperará automáticamente. ¡Por lo tanto, el simple hecho de actualizar a JDK 12 acelera el lanzamiento de la aplicación!
344: Colecciones mixtas abortables para G1
Característica interior Para ser honesto No entiendo nada en el trabajo de G1 Una explicación de las características de GC es una tarea ingrata. Requiere una comprensión de los detalles de su trabajo tanto del explicador como de la comprensión. Para la mayoría de las personas, GC es una especie de infierno fuera de una caja de rapé que puedes engañar en caso de algo. Por lo tanto, el problema debe explicarse de alguna manera más simple.
Problema : G1 podría funcionar mejor.
Bueno, el problema es que el GC es un compromiso de muchos parámetros, uno de los cuales es la duración de la pausa. A veces, la pausa es demasiado larga, y luego es bueno poder cancelarla.
¿Cuándo sucede esto? G1 realmente analiza el comportamiento de la aplicación y selecciona el frente del trabajo (expresado como un conjunto de recopilación ) en función de sus conclusiones. Cuando se aprueba el alcance del trabajo, G1 se compromete a recoger todos los objetos vivos en el conjunto de colección, obstinadamente y sin parar, en una sola sesión. A veces lleva demasiado tiempo. En esencia, esto significa que G1 calculó incorrectamente la cantidad de trabajo. Puede engañarlo cambiando repentinamente el comportamiento de su aplicación para que la heurística funcione además de los datos incorrectos cuando demasiadas regiones antiguas ingresan al conjunto de recopilación.
Para salir de la situación, G1 se finalizó mediante el siguiente mecanismo: si la heurística selecciona regularmente la cantidad incorrecta de trabajo, G1 cambia a recolección de basura incremental, paso a paso, y cada paso siguiente (si no encaja en el tiempo de ejecución objetivo) puede cancelarse. No tiene sentido recopilar algo de forma incremental (regiones jóvenes), por lo tanto, todo ese trabajo se resalta en el bloque "obligatorio", que todavía se lleva a cabo de forma continua.
¿Qué hacer con el usuario final? Nada, necesita actualizar a JDK 12, todo mejorará por sí solo.
346: Devuelva rápidamente la memoria comprometida no utilizada de G1
Característica interior El problema es que si tenemos una gran cadera que nadie usa activamente, parece justo devolver toda esta memoria inactiva al sistema operativo. Antes de JDK 12, sin embargo, esto no sucedió.
Para lograr su objetivo en términos de la longitud de pausa permitida, G1 realiza un conjunto de ciclos incrementales, paralelos y de etapas múltiples. En JDK 11, proporciona memoria comprometida al sistema operativo solo con GC completo, o durante la fase de marcado paralelo. Si conecta el registro (-Xloggc: /home/gc.log -XX: + PrintGCDetails -XX: + PrintGCDateStamps), esta fase se muestra de la siguiente manera:
8801.974: [G1Ergonomics (Concurrent Cycles) request concurrent cycle initiation, reason: occupancy higher than threshold, occupancy: 12582912000 bytes, allocation request: 0 bytes, threshold: 12562779330 bytes (45.00 %), source: end of GC] 8804.670: [G1Ergonomics (Concurrent Cycles) initiate concurrent cycle, reason: concurrent cycle initiation requested] 8805.612: [GC concurrent-mark-start] 8820.483: [GC concurrent-mark-end, 14.8711620 secs]
Lo curioso es que G1, como puede, lucha con paradas completas, y el ciclo concurrente comienza solo con asignaciones frecuentes y un montón obstruido. Nuestra situación, cuando nadie toca la cadera, es todo lo contrario. ¡Las situaciones en que G1 se rasca para dar memoria al sistema operativo ocurrirán muy raramente!
Por lo tanto, todos habrían puntuado en este problema ("¡compre aún más RAM, que es como un pícaro!"). Si no fuera por uno, hay todo tipo de nubes y contenedores en los que esto significa una utilización insuficiente y la pérdida de dinero serio. Mira, qué informe tan genial , lleno hasta el borde de dolor.
La solución fue enseñarle a G1 a comportarse bien en este caso particular, como ya saben Shenanda o GenCon de OpenJ9. Es necesario determinar la utilización insuficiente de la cadera y, en consecuencia, reducir su uso. En algunas pruebas en Tomcat, esto permitió reducir el consumo de memoria a casi la mitad.
La conclusión es que la aplicación se considera inactiva, o si el intervalo (en milisegundos) ha getloadavg()
desde la última compilación y no hay un ciclo concurrente, o si getloadavg()
durante un período de un minuto mostró una carga por debajo de un cierto umbral. Tan pronto como sucede esto, comienza la recolección periódica de basura: ciertamente no limpiará tan bien como el ensamblaje completo, pero afectará la aplicación de manera mínima.
Puede insertarlo en este registro:
(1) [6.084s][debug][gc,periodic ] Checking for periodic GC. [6.086s][info ][gc ] GC(13) Pause Young (Concurrent Start) (G1 Periodic Collection) 37M->36M(78M) 1.786ms (2) [9.087s][debug][gc,periodic ] Checking for periodic GC. [9.088s][info ][gc ] GC(15) Pause Young (Prepare Mixed) (G1 Periodic Collection) 9M->9M(32M) 0.722ms (3) [12.089s][debug][gc,periodic ] Checking for periodic GC. [12.091s][info ][gc ] GC(16) Pause Young (Mixed) (G1 Periodic Collection) 9M->5M(32M) 1.776ms (4) [15.092s][debug][gc,periodic ] Checking for periodic GC. [15.097s][info ][gc ] GC(17) Pause Young (Mixed) (G1 Periodic Collection) 5M->1M(32M) 4.142ms (5) [18.098s][debug][gc,periodic ] Checking for periodic GC. [18.100s][info ][gc ] GC(18) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 1.685ms (6) [21.101s][debug][gc,periodic ] Checking for periodic GC. [21.102s][info ][gc ] GC(20) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 0.868ms (7) [24.104s][debug][gc,periodic ] Checking for periodic GC. [24.104s][info ][gc ] GC(22) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 0.778ms
Resuelto? Yo no En JEP, hay una traducción detallada del lenguaje de señas de cada línea del registro, y cómo funciona el algoritmo, y todo lo demás.
"Y qué, ¿por qué lo descubrí?" - usted pregunta Ahora tenemos dos identificadores adicionales: G1PeriodicGCInterval
y G1PeriodicGCSystemLoadThreshold
, que se puede torcer cuando se pone mal. Es seguro que algún día será malo, ¡es Java, bebé!
Resumen
Como resultado, tenemos un fuerte lanzamiento en nuestras manos, no una revolución, sino una evolución centrada en mejorar el rendimiento. Exactamente la mitad de las mejoras están relacionadas con el rendimiento: tres JEP sobre GC y uno sobre CDS, que prometen activarse por sí mismos, solo necesitan actualizarse a JDK 12. Además, obtuvimos una función de idioma (expresiones de cambio), dos nuevas herramientas para desarrolladores de JDK ( Constantes API y pruebas JMH), y ahora la comunidad puede enfocarse mejor en un solo puerto de 64 bits en ARM.
En general, actualice a JDK 12 ahora, y que la Fuerza lo acompañe. Lo necesitarás
Minuto de publicidad. Muy pronto, del 5 al 6 de abril, se llevará a cabo la conferencia JPoint, que reunirá a una gran cantidad de personas que saben mucho sobre JDK y todo tipo de nuevas características. Por ejemplo, seguramente habrá Simon Ritter de Azul con una conferencia sobre “JDK 12: trampas para los incautos” . ¡El lugar más apropiado para discutir la última versión! Puede obtener más información sobre JPoint en el sitio web oficial .