¡Ha llegado el momento de Java 12! Revisión de los JEP calientes


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 .

Source: https://habr.com/ru/post/444434/


All Articles