¿Qué tan dulces son las pausas de GC de submilisegundos? Primera experiencia de JDK17+ZGC|Dewu Technology

1. Introducción

La pausa del recolector de basura siempre ha sido el foco de atención de los ingenieros de Java, especialmente para servicios con altos requisitos de respuesta en tiempo real, el tiempo de pausa de decenas de milisegundos o incluso cientos de milisegundos de los recolectores de basura convencionales como CMS y G1 es bastante fatal. . Además, el umbral de sintonización es relativamente alto y es necesario tener una cierta comprensión del mecanismo interno del recolector de basura para poder realizar una sintonización efectiva. Para resolver tales problemas, JDK 11 comenzó a introducir un recolector de basura de baja latencia ZGC. ZGC utiliza algunas tecnologías nuevas y algoritmos de optimización, que pueden controlar el tiempo de pausa del GC en 10 milisegundos, y con el soporte de JDK 17, ¡el tiempo de pausa del ZGC puede incluso controlarse en el nivel de submilisegundos!

2 ZGC

Ya hay muchos artículos similares en Internet sobre la introducción y el principio de ZGC, así que aquí hay solo una breve introducción.

2.1 Objetivos del diseño

ZGC se introdujo por primera vez como una característica experimental en JDK 11 y se declaró listo para producción en JDK 15. Como recolector de elementos no utilizados de baja latencia, está diseñado para cumplir los siguientes objetivos:

  • Compatibilidad con tamaño de pila de 8 MB a 16 TB

  • GC máx. de 10 ms temporalmente

  • En el peor de los casos, el rendimiento se reducirá en un 15 % (vale la pena la baja latencia para el rendimiento y la expansión del rendimiento puede resolverse)

1.png

2.2 Distribución de memoria ZGC

ZGC es diferente al CMS tradicional y G1, no tiene el concepto de generación, solo tiene la probabilidad de Región similar a G1, la Región de ZGC puede tener tres tipos de capacidad como se muestra en la siguiente figura:

  • Región Pequeña (Small Region): La capacidad está fijada en 2 MB, y se utiliza para colocar objetos pequeños de menos de 256 KB.

  • Región mediana (Medium Region): La capacidad se fija en 32 MB, que se utiliza para colocar objetos de más de 256 KB pero de menos de 4 MB.

  • Región grande (Large Region): La capacidad no es fija y se puede cambiar dinámicamente, pero debe ser un múltiplo entero de 2 MB, que se utiliza para colocar objetos grandes de 4 MB o más. Cada región grande almacenará un objeto grande, lo que también indica que aunque el nombre es "región grande", su capacidad real puede ser menor que la de una región mediana, y la capacidad mínima puede ser tan baja como 4 MB. Las regiones grandes no se redistribuirán en la implementación de ZGC (la redistribución es una acción de procesamiento de ZGC, que se utiliza en la etapa de recopilación de la copia de objetos) porque el costo de copiar objetos grandes es muy alto.

2.png

2.3 Proceso de trabajo del GC

Al igual que ParNew y G1 en CMS, ZGC también usa el algoritmo de marcado y copia, pero ZGC resuelve el problema de acceder con precisión a los objetos durante el proceso de transferencia a través de punteros de colores y tecnología de barrera de lectura, y casi todos son concurrentes en el marcado, transferencia y las etapas de reubicación Ejecución, que es la razón más crítica para que ZGC logre el objetivo de un tiempo de pausa de menos de 10 ms.

3.png

Como se puede ver en la figura anterior, ZGC tiene solo tres etapas STW: marcado inicial, remarcado y transferencia inicial. Para el proceso de transferencia específico, hay muchos artículos similares en Internet, por lo que no daré una introducción detallada aquí. Si está interesado, puede consultar los siguientes artículos:

Exploración y práctica de la nueva generación de recolector de basura ZGC ZGC La última generación de recolector de basura | Programador avanzado

3 ¿Por qué elegir JDK17?

Lanzado el 14 de septiembre, JDK 17 es una versión de soporte a largo plazo (LTS), lo que significa que será compatible y actualizado durante muchos años. Esta es también la primera versión LTS que incluye una versión ZGC lista para producción. En resumen, se incluyó una versión experimental de ZGC en JDK 11 (la versión anterior de LTS), mientras que la primera versión de ZGC lista para producción apareció en JDK 15 (una versión que no es LTS).

4 Proceso de actualización

La actualización de JDK8+G1 a JDK17+ZGC es principalmente para la adaptación a nivel de código y de parámetros de inicio de JVM.

4.1 Descarga de JDK

En primer lugar, jdk17 elige openjdk, dirección de descarga: https://jdk.java.net/archive/, seleccione la versión 17 GA

4.png

4.2 Adaptación del código

  • JDK11 elimina los módulos Java EE y CORBA

Si usa paquetes que comienzan con javax.annotation.*, javax.xml.*, etc. en el proyecto, debe introducir manualmente las dependencias correspondientes

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
</dependency>

  • Actualización de versión de dependencia relacionada con Maven
<!-- 仅供参考 -->
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
<maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
<maven-deploy-plugin.version>3.0.0-M1</maven-deploy-plugin.version>
<maven-release-plugin.version>3.0.0-M1</maven-release-plugin.version>
<maven-site-plugin.version>3.9.1</maven-site-plugin.version>
<maven-enforcer-plugin.version>3.0.0-M2</maven-enforcer-plugin.version>
<maven-project-info-reports-plugin.version>3.1.0</maven-project-info-reports-plugin.version>
<maven-plugin-plugin.version>3.6.1</maven-plugin-plugin.version>
<maven-javadoc-plugin.version>3.3.0</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
<maven-jxr-plugin.version>3.0.0</maven-jxr-plugin.version>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
   <!-- <version>1.16.20</version>-->
    <version>1.18.22</version>
</dependency>

5.png

  • Después de modularizar Java9, la aplicación no puede ver todas las clases del JDK, lo que afectará el funcionamiento de algunos reflejos. Debe resolverse con el siguiente comando
--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED

  • Después de usar transmittable-thread-local-2.14.2.jar localmente, se informa un error al iniciar

6.png

Se puede resolver agregando la salida de registro después del agente.En cuanto a la razón, la suposición es que tiene algo que ver con el orden de carga de clases

-javaagent:/Users/admin/Documents/transmittable-thread-local-2.14.2.jar
=ttl.agent.logger:STDOUT

El contenido anterior es solo para los problemas encontrados en la actualización del proyecto Rainbow Bridge. La adaptación de diferentes códigos comerciales puede ser diferente, y es necesario encontrar una solución de acuerdo con la situación real.

4.3 Reemplazo de parámetros JVM

Los siguientes son algunos parámetros generales de GC y parámetros específicos de ZGC y algunas selecciones de diagnóstico de ZGC, del sitio web oficial: Main - Main - OpenJDK Wiki

13.png

El significado específico de cada parámetro no se presenta aquí. Puede consultar el documento del sitio web oficial The java Command, que contiene instrucciones detalladas.

Los parámetros de inicio de JKD8+G1:

-server -Xms36600m -Xmx36600m
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:+PrintReferenceGC
-XX:+ParallelRefProcEnabled
-XX:G1HeapRegionSize=16m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/opt/apps/errorDump.hprof
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintHeapAtGC
-XX:+PrintGCApplicationConcurrentTime
-verbose:gc
-Xloggc:/opt/apps/logs/${app_name}-gc.log

Los parámetros de inicio de JDK17+ZGC son los siguientes:

-server -Xms36600m -Xmx36600m
#开启ZGC
-XX:+UseZGC 
#GC周期之间的最大间隔(单位秒)
-XX:ZCollectionInterval=120
#官方的解释是 ZGC 的分配尖峰容忍度,数值越大越早触发GC
-XX:ZAllocationSpikeTolerance=4
#关闭主动GC周期,在主动回收模式下,ZGC 会在系统空闲时自动执行垃圾回收,以减少垃圾回收在应用程序忙碌时所造成的影响。如果未指定此参数(默认情况),ZGC 会在需要时(即堆内存不足以满足分配请求时)执行垃圾回收。
-XX:-ZProactive 
#GC日志
-Xlog:safepoint=trace,classhisto*=trace,age*=info,gc*=info:file=/opt/logs/gc-%t.log:time,level,tid,tags:filesize=50M 
#发生OOM时dump内存日志
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/opt/apps/errorDump.hprof

5 Resultados de la prueba de presión

directamente en el mapa

7.png

8.png

9.png

Como se describe en los objetivos de diseño de ZGC, reduce los tiempos de pausa del GC de decenas de milisegundos en el pasado al increíble nivel de submilisegundos. Sin embargo, este rendimiento de latencia ultrabaja también requiere un cierto precio, porque ZGC ocupará una cierta cantidad de recursos de CPU mientras logra una baja latencia. Normalmente, ZGC no ocupa más del 15% de la CPU. En el proyecto Rainbow Bridge, después de usar los parámetros de JVM recomendados anteriormente, el recurso de CPU ocupado por ZGC es de aproximadamente el 6 %.

Registro de 6 ZGC

6.1 Salida de registro ZGC

El registro del GC contiene información detallada sobre las operaciones del GC, que puede ayudarnos a analizar los problemas actuales del GC. Primero mire los parámetros sobre los registros de GC en los parámetros de JVM anteriores

-Xlog:safepoint=trace,classhisto*=trace,age*=info,gc*=info:file=/opt/logs/gc-%t.log:time,level,tid,tags:filesize=50M


  • safepoint=trace: registrar registros de nivel de seguimiento sobre safepoint. Safepoint es un estado especial en JVM, que se usa para garantizar que todos los subprocesos entren en un estado seguro antes de ciertas operaciones (como recolección de basura, optimización de código, etc.).

  • classhisto*=trace: Registre registros de nivel de seguimiento relacionados con el historial de clases. age*=info: registrar registros de nivel de información relacionados con la edad del objeto (el tiempo que existió en la generación joven).

  • gc*=info: registrar registros de nivel de información relacionados con la recolección de basura.

  • file=/opt/logs/gc-%t.log: escriba el registro en el archivo en el directorio /opt/logs/, el nombre del archivo es gc-%t.log, donde %t es un marcador de posición que indica la marca de tiempo actual .

  • time,level,tid,tags: Incluya la marca de tiempo, el nivel de registro, la identificación del hilo y las etiquetas en cada registro.

  • tamaño del archivo = 50M: establezca el límite de tamaño del archivo de registro en 50 MB. Cuando el tamaño del archivo de registro alcance este límite, la JVM creará un nuevo archivo de registro y continuará con el registro.

Para obtener una configuración de registro gc más detallada, consulte: https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#enable-logging-with-the-jvm-unified -marco de registro

6.2 Registro de claves STW

Entre ellos, nos centramos en la situación STW de GC. Las siguientes son algunas palabras clave que representan la etapa STW de GC.

  • Las tres etapas más básicas de STW, marca inicial: pausa, marca de inicio en el registro, comentario: pausa, marca de fin en el registro, transferencia inicial: pausa, reubica, inicio en el registro.

10.png

  • Bloqueo de asignación de memoria: esto generalmente se debe a que la velocidad de producción de basura es mayor que la velocidad de reciclaje, y la basura llega demasiado tarde para reciclar. Cuando el montón de basura está lleno, el subproceso se bloqueará y esperará a que se complete el GC. La palabra clave es Puesto de asignación (el nombre del hilo bloqueado)

11.jpeg

Si aparecen dichos registros, puede probar las siguientes soluciones:

  1. -XX:ZCollectionInterval El significado de la configuración: el intervalo máximo entre dos ciclos de GC (en segundos). De forma predeterminada, esta opción está establecida en 0 (deshabilitada), y esta configuración se puede ajustar adecuadamente para acortar el ciclo del GC y mejorar la velocidad de recolección de elementos no utilizados, pero esto aumentará el uso de la CPU de la aplicación.

  2. La explicación oficial de -XX:ZAllocationSpikeTolerance es la tolerancia máxima de asignación de ZGC. De hecho, cuanto mayor sea el valor, antes se activará el reciclaje. Esta configuración se puede ajustar adecuadamente para activar el reciclaje antes y mejorar la velocidad de la recolección de elementos no utilizados, pero esto aumentará el uso de la CPU de la aplicación.

  • Punto seguro: GC solo se puede realizar después de que todos los subprocesos ingresen al punto seguro, y ZGC ingresa regularmente al punto seguro para juzgar si se requiere GC. El subproceso que ingresa al punto seguro primero debe esperar al subproceso que ingresa al punto seguro hasta que se suspendan todos los subprocesos. Registrar palabras clave punto seguro... detenido

  • Volcado de subprocesos y memoria: como los comandos jstack y jmap, generalmente provocados por un volcado manual, la palabra clave de registro HeapDumper

7 memoria de página enorme de Linux

También se puede ver en el sitio web oficial de openjdk que habilitar la memoria de página grande de Linux mejorará el rendimiento de la aplicación.

12.png

Para conocer el método de apertura, consulte el documento del sitio web oficial https://wiki.openjdk.org/display/zgc/Main#Main-EnablingLargePagesOnLinux Tenga en cuenta que además de modificar la configuración del sistema, también debe agregar -XX:+UseLargePages configuración en el proceso JVM parámetros de inicio

Después de varias rondas de pruebas de esfuerzo, se encontró que después de que se habilita la página enorme de Linux, la CPU se reduce en aproximadamente un 8 %. ser alto. Además, no existe otra aplicación en el entorno de producción que habilite esta configuración, la estabilidad debe ser estudiada, el entorno de producción puede evaluar si habilitarlo por sí mismo.

8 resumen

En este artículo, exploramos cómo actualizar a JDK 17 y usar la última generación del recolector de basura ZGC. Después de la práctica y las pruebas, descubrimos que el sistema actualizado funcionó bien en la recolección de basura y que el tiempo de pausa se controló de manera efectiva en 1 milisegundo. Aunque este proceso de optimización puede consumir recursos adicionales de la CPU, los tiempos de pausa ultrabajos del GC obtenidos obviamente valen mucho la pena. En resumen, en comparación con otros recolectores de basura, el rendimiento y la estabilidad de ZGC ya son muy buenos y no requieren muchos ajustes. En la mayoría de los casos, se puede obtener un rendimiento excelente utilizando la configuración predeterminada recomendada oficialmente por ZGC. Para aquellas aplicaciones sensibles a RT, actualizar a JDK 17 y adoptar ZGC es una buena elección.

Texto: Shinichi

Este artículo pertenece al original de la tecnología Dewu, fuente: sitio web oficial de la tecnología Dewu

¡Está estrictamente prohibido reimprimir sin el permiso de Dewu Technology, de lo contrario, se investigará la responsabilidad legal de acuerdo con la ley!

{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/5783135/blog/10083247
Recomendado
Clasificación