Análisis en profundidad del recolector de basura G1 y optimización del rendimiento

Este artículo presenta en detalle la configuración de parámetros del recolector de basura G1, cómo realizar ajustes del rendimiento y cómo analizar y evaluar el rendimiento del GC.

0. Introducción a G1

El nombre completo de G1 es Garbage First Garbage Collector , que es un recolector de basura del lado del servidor integrado en HotSpot JVM.
G1 utiliza el [algoritmo generacional] para desarmar el proceso de GC en múltiples etapas concurrentes y paralelas, y dividir el tiempo de pausa, logrando así características de baja latencia y manteniendo un buen rendimiento.
Siempre que G1 piense que se puede realizar la recolección de basura, se activará un GC. Por supuesto, G1 dará prioridad a recuperar áreas con menos datos supervivientes.
Menos datos supervivientes significan más objetos basura en su interior, lo que también es el origen del nombre Garbage First.

El recolector de basura es esencialmente una herramienta de administración de memoria. El algoritmo G1 implementa principalmente la gestión automática de la memoria de las siguientes formas:

  • [Generación] Se asignan nuevos objetos en la generación joven, y los objetos que alcanzan cierta edad se promueven a la generación anterior.
  • [Concurrencia] Atraviesa todos los objetos supervivientes de la generación anterior durante la fase de marcado concurrente. Siempre que el uso total de la memoria dinámica en Java excede un umbral, HotSpot activa un ciclo de marcado.
  • [Organización] Organice los objetos supervivientes mediante copia paralela para liberar memoria disponible.

En GC, paralelo (paralelo) se refiere a múltiples subprocesos de GC que trabajan juntos, y concurrente (concurrente) se refiere a la ejecución simultánea de subprocesos de GC y subprocesos comerciales.

Este artículo primero presenta brevemente cómo configurar los parámetros G1 y luego presenta cómo analizar y evaluar el rendimiento del GC.
Si desea realizar un ajuste de GC, al menos debe tener cierta comprensión del mecanismo de recolección de basura de Java .

G1 es un recolector de basura generacional incremental. ¿Qué es un incremento?
G1 divide la memoria del montón en muchas [áreas pequeñas, bloques pequeños] (regiones) del mismo tamaño.
Cuando se inicia la JVM, el tamaño de cada región se determina de acuerdo con la configuración de la memoria del montón. El rango de tamaño de la región es 1MBde hasta 32MB, y el número total generalmente no excede 2048.

En G1, la nueva generación (eden), el área de supervivencia (sobreviviente) y la vieja generación (vieja generación) son conceptos lógicos, que se componen de estas regiones, y estas regiones no necesitan ser continuas.

https://img-blog.csdn.net/20170205235146220

Puede configurar parámetros para especificar el "tiempo de pausa máximo esperado" y G1 intentará alcanzar este valor objetivo suave en tiempo real.
Durante la recolección de basura en [modo joven puro (joven)], G1 puede ajustar dinámicamente el tamaño de la generación joven (eden + sobreviviente) para lograr este tiempo de pausa objetivo suave en tiempo real.
En el proceso de recolección de basura [modo mixto (mixto)], G1 puede ajustar la cantidad de regiones antiguas que deben reciclarse en este GC, dependiendo del [número total de regiones a reciclar], [el porcentaje de supervivientes objetos en cada región], y [proporción de desperdicio permitido de memoria del montón] y otros datos.

G1 adopta [Copia paralela incremental] para implementar la [Función de desfragmentación de memoria dinámica] y copia los objetos supervivientes de la colección en la nueva región, el conjunto de regiones involucradas.
El objetivo es recuperar la mayor cantidad de memoria dinámica posible de las regiones libres y, al mismo tiempo, intentar lograr la métrica de tiempo de pausa deseada.

G1 configura un [conjunto de memoria] separado para cada región. El nombre en inglés es Conjunto recordado, o RSet para abreviar, que se utiliza para rastrear y registrar referencias de otras regiones a esta región.
A través de esta división de regiones y la estructura de datos RSet independiente, G1 puede realizar una recolección de basura incremental en paralelo sin atravesar toda la memoria del montón.
Debido a que solo necesita escanear el RSet, puede saber qué referencias entre regiones apuntan a esta región y luego reciclar estas regiones.
G1 utiliza [barrera posterior a la escritura] (barrera posterior a la escritura) para registrar la información de modificación de la memoria del montón y es responsable de actualizar RSet.

1. Introducción a la fase de recolección de basura.

El GC en modo de generación joven pura del recolector de basura G1 y el GC en modo mixto, además de la fase STW de la pausa de transferencia (pausa de evacuación), existen ciclos de marcado paralelos y concurrentes que constan de múltiples subfases.
G1 utiliza el algoritmo de instantánea de inicio (SATB, instantánea al comienzo) para tomar una instantánea de la información del objeto superviviente en la memoria del montón al comienzo del ciclo de marcado.
El total de objetos supervivientes incluye los objetos supervivientes en la instantánea inicial, además de los objetos recién creados desde que comenzó la marca.
El algoritmo de marcado de G1 utiliza [barrera de preescritura] (barrera de preescritura) para registrar y marcar objetos que lógicamente pertenecen a esta instantánea.

2. Recolección de basura en modo puramente joven.

G1 envía la mayoría de las solicitudes de asignación de memoria al área del Edén.
Durante el proceso de recolección de basura en el modo de generación joven, G1 recolectará el área del Edén y el área de supervivencia utilizada por el GC anterior.
Y copiar/transferir los objetos supervivientes a algunas regiones nuevas, donde la copia específica depende de la edad de los objetos;
si alcanza una cierta edad de GC, se transferirá/promocionará a la generación anterior; de lo contrario, se transferirá a la zona de supervivencia.
El área de supervivencia esta vez se agregará al CSet del GC/GC de modo mixto de próxima generación joven.

3. Recolección de basura en modo mixto

Una vez ejecutado el ciclo de calificación concurrente, G1 cambiará del modo joven puro al modo mixto.
Al realizar la recolección de basura en modo mixto, G1 seleccionará una parte de la región de generación anterior para agregarla a la recolección de reciclaje. Por supuesto, cada recolección de reciclaje incluye todas las áreas del Edén y las áreas de supervivencia.
Específicamente, más adelante se analizará cuántas regiones de generación anterior se agregan a la vez y qué parámetros determinan.
Después de muchas recolecciones de basura en modo mixto, muchas regiones de generación anterior ya se han procesado y luego G1 vuelve al modo puro de generación joven hasta que se completa el siguiente ciclo de marcado concurrente.

4. Fases del ciclo de calificación

El ciclo de calificación de G1 consta de las siguientes fases:

  • [Fase de marcado inicial] ( Initial mark phase): El marcado de las raíces de GC en esta etapa generalmente se realiza además de un GC normal de generación joven.
  • [Escanee la región donde se encuentra la raíz del GC] ( Root region scanning phase): De acuerdo con los elementos raíz del GC determinados en la etapa de marcado inicial, escanee la región donde se encuentran estos elementos, obtenga referencias a la generación anterior y marque los objetos referenciados. Esta fase se ejecuta al mismo tiempo que los subprocesos de la aplicación, es decir, sin pausas STW, y debe completarse antes de que comience el GC de la próxima generación joven.
  • [Fase de marcado concurrente] ( Concurrent marking phase)": recorre todo el montón para encontrar todos los objetos supervivientes accesibles. Esta fase se ejecuta al mismo tiempo que el subproceso de la aplicación y también puede ser interrumpida por el GC de generación joven.
  • 【Fase de remarcado】( Remark phase): Hay una pausa STW en esta fase para completar el ciclo de marcado. G1 borrará el búfer SATB, rastreará los objetos supervivientes no alcanzados y realizará el procesamiento de referencias.
  • 【Etapa de limpieza】( Cleanup phase): Esta es la última subetapa. G1 tendrá una pausa STW al realizar estadísticas y limpiar RSet. Durante el proceso estadístico, se marcarán las regiones completamente inactivas y también se marcarán las regiones candidatas adecuadas para GC en modo mixto. Parte de la fase de limpieza se realiza simultáneamente, como cuando las regiones libres se restablecen y se agregan a la lista libre.

5. Parámetros comunes y valores predeterminados

G1 es un recolector de basura adaptativo. La mayoría de los parámetros tienen valores predeterminados. Generalmente, puede ejecutarse de manera eficiente sin mucha configuración.
Los parámetros de uso común y los valores predeterminados correspondientes se enumeran a continuación. Si existen requisitos especiales, puede ajustar los parámetros de inicio de JVM para cumplir con indicadores de rendimiento específicos.

-XX:G1HeapRegionSize=n

Se utiliza para establecer el tamaño de la región G1. Debe ser 2的幂(potencia de x), el rango permitido 1MBes 32MB. El valor predeterminado de este parámetro se ajustará dinámicamente de acuerdo con el tamaño inicial ( ) y el valor máximo ( )
de la memoria del montón , para dividir la memoria del montón en aproximadamente 2048 regiones.-Xms-Xmx

-XX:MaxGCPauseMillis=200

El tiempo máximo de pausa deseado. El valor predeterminado es 200 milisegundos. Este valor no se ajustará automáticamente, es el que se establezca al inicio.

-XX:G1NewSizePercent=5

Establezca la proporción de espacio mínima para la generación joven. El valor predeterminado 5es equivalente a que al menos el 5% de la memoria del montón se utilice como generación joven.
Este parámetro anula -XX:DefaultMinNewGenPercent.
Este es un parámetro experimental y puede cambiar en versiones posteriores.

-XX:G1MaxNewSizePercent=60

Establezca la relación de espacio máxima de la generación joven. El valor predeterminado 60equivale a un máximo del 60% de la memoria del montón que se utilizará como generación joven.
Esta configuración anula -XX:DefaultMaxNewGenPercent.
Este es un parámetro experimental y puede cambiar en versiones posteriores.

-XX:ParallelGCThreads=n

Establezca el número de subprocesos de trabajo paralelos en la fase STW.

  • Si la cantidad de procesadores lógicos es menor o igual a 8, es nigual a la cantidad de procesadores lógicos de forma predeterminada.
  • Si la cantidad de procesadores lógicos es mayor que 8, el nvalor predeterminado es aproximadamente igual a la cantidad de procesadores 5/8+ 3.
  • Si se trata de un sistema SPARC de alta configuración, el valor predeterminado nes aproximadamente igual a la cantidad de procesadores lógicos 5/16.
  • En la mayoría de los casos utilice el valor predeterminado.
  • Hay una excepción, es decir, se utiliza una versión inferior de JDK en el contenedor Docker. Para la referencia del caso: JVM Troubleshooting and Analysis Part II (case combat) .

-XX:ConcGCThreads=n

Establece el número de subprocesos de GC para el marcado simultáneo. El valor predeterminado es aproximadamente igual al ParallelGCThreadsvalor de 1/4.

-XX:InitiatingHeapOccupancyPercent=45

Establece el umbral de activación para el ciclo de marcado, que es el porcentaje de uso de la memoria del montón de Java. El umbral de activación predeterminado es todo el montón de Java 45%.

-XX:G1MixedGCLiveThresholdPercent=65

Al realizar GC en modo mixto, determine si incluirlo en la colección de acuerdo con la tasa de uso de la región de generación anterior. El umbral predeterminado es 65%.
Esta configuración anula -XX:G1OldCSetRegionLiveThresholdPercent.
Este es un parámetro experimental y puede cambiar en versiones posteriores.

-XX:G1HeapWastePercent=10

Establece el porcentaje tolerable de desperdicio de memoria del montón.
Si la proporción de memoria de montón recuperable es menor que esta proporción de umbral, HotSpot no iniciará la GC en modo mixto.
El valor predeterminado es 10%.

-XX:G1MixedGCCountTarget=8

Cuántos GC de modo mixto se espera que funcionen después de que se complete un ciclo de marcado hasta que la fracción de datos en vivo caiga por debajo de G1MixedGCLiveThresholdPercent.
El valor predeterminado es realizar 8 GC en modo mixto. El número de ejecuciones específicas es generalmente menor que este valor.

-XX:G1OldCSetRegionThresholdPercent=10

En GC de modo mixto, el límite superior del número de regiones antiguas procesadas cada vez. El valor predeterminado es el montón de Java 10%.

-XX:G1ReservePercent=10

Establece un cierto porcentaje de espacio reservado para mantenerlo libre y reducir to空间el riesgo de memoria insuficiente. El valor predeterminado es 10%.
Aunque se trata de un porcentaje, en realidad está asignado a un tamaño específico, por lo que al aumentar o disminuir el porcentaje, es mejor ajustar el tamaño total del montón de Java al mismo tamaño.

6. Cómo desbloquear parámetros JVM experimentales

Para modificar el valor de un parámetro JVM experimental, primero se debe declarar.
Podemos especificarlo explícitamente en los parámetros de la línea de comando antes de configurar los parámetros experimentales -XX:+UnlockExperimentalVMOptions. Por ejemplo:

java -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=10 -XX:G1MaxNewSizePercent=75 G1test.jar

7. Mejores prácticas y recomendaciones

Antes de ajustar los parámetros del G1, hay algunas cosas a tener en cuenta:

  • 禁止设置年轻代的大小: No utilice opciones como -Xmn, , -XX:NewRatioetc. para especificar el tamaño de la generación joven. Si especifica un tamaño fijo de generación joven, anulará el objetivo de tiempo de pausa máximo, que se puede decir que supera la ganancia.
  • 期望的最大暂停时间值: Independientemente del recolector de basura que se ajuste, existe un equilibrio entre la latencia y las métricas de rendimiento.
    G1 es un recolector de basura incremental con un tiempo de pausa uniforme, por lo que la sobrecarga de recursos de la CPU es relativamente grande. El objetivo de rendimiento de G1 se refiere a garantizar que los subprocesos de la aplicación ocupen más del 90% del tiempo de la CPU en escenarios de alta carga y que la sobrecarga de los subprocesos de GC se mantenga por debajo del 10%.
    Por el contrario, el recolector de basura de alto rendimiento integrado en HotSpot se puede optimizar al 99% del tiempo del subproceso de la aplicación, lo que significa que hay menos del 1% de la sobrecarga de GC.
    Por lo tanto, al medir el índice de rendimiento de G1, es necesario relajar el índice de tiempo de pausa. Establecer un objetivo de tiempo de pausa que sea demasiado pequeño significa que está dispuesto a incurrir en una gran sobrecarga de GC, pero esto afectará el rendimiento. Al realizar pruebas de esfuerzo del índice de latencia de G1, puede establecer el índice de tiempo de pausa suave en tiempo real esperado y G1 hará todo lo posible para lograr este objetivo. El efecto secundario es que el rendimiento se verá afectado.
  • Para la mayoría de las aplicaciones del lado del servidor, la carga de la CPU no excederá el 50%. Incluso si el GC ocupa un poco más de CPU, no afectará mucho, porque todavía hay mucha redundancia. Prestamos más atención al tiempo de pausa del GC, porque está relacionado con las métricas de latencia de respuesta.
  • 混合模式的GC: Al ajustar un GC en modo mixto, se pueden probar las siguientes opciones. Consulte las secciones anteriores para obtener detalles sobre estas opciones:
    • -XX:InitiatingHeapOccupancyPercent: Establece el umbral de activación para el ciclo de marcado.
    • -XX:G1MixedGCLiveThresholdPercenty -XX:G1HeapWastePercent: Ajustar estrategias relacionadas con GC de modo mixto.
    • -XX:G1MixedGCCountTargety -XX:G1OldCSetRegionThresholdPercentse utilizan para optimizar y ajustar la proporción de la región de generación anterior en el CSet.

8. Mensajes de desbordamiento y agotamiento de la memoria en el registro de GC

Si vemos esto en los registros de GC to-space overflow/exhausted, significa que G1 no tiene suficiente memoria ni para el área en vivo ni para los objetos que deben promocionarse, o ambos. En este momento, la memoria dinámica de Java generalmente ha alcanzado el valor máximo y no se puede expandir automáticamente. Los ejemplos son los siguientes:

924.897: [GC pause (G1 Evacuation Pause) (mixed) (to-space exhausted), 0.1957310 secs]

o esto:

924.897: [GC pause (G1 Evacuation Pause) (mixed) (to-space overflow), 0.1957310 secs]

Para solucionar estos problemas, puede probar los siguientes ajustes:

  • Aumente -XX:G1ReservePercentel valor de la opción para aumentar el tamaño reservado "al espacio". En términos generales, el tamaño total de la memoria del montón debe aumentarse en consecuencia.
  • Bájelo -XX:InitiatingHeapOccupancyPercentpara activar los ciclos de marcado antes.
  • Aumente adecuadamente -XX:ConcGCThreadsel valor de la opción para aumentar el número de hilos de marcado simultáneos.

Para obtener información específica sobre estas opciones, consulte la descripción anterior.

9. Asignación de memoria para objetos grandes/objetos enormes

Si un objeto excede la mitad del espacio de una sola región, G1 lo considerará un objeto enorme. Por ejemplo, una matriz muy grande o String.
Estos objetos se asignarán directamente a la "región enorme" de la vieja generación. Una región de objeto grande es un grupo de regiones contiguas en el espacio de direcciones virtuales. StartsHumongousMarca la primera región y ContinuesHumongousmarca el conjunto de regiones posterior.

Antes de asignar regiones de objetos grandes, G1 primero determinará si se alcanza el umbral para iniciar el ciclo de marcado y, si es necesario, iniciará un ciclo de marcado simultáneo.

Durante la fase de limpieza al final del ciclo de marcado y durante la limpieza de FullGC, se liberan objetos gigantes que ya no están en uso.

Para reducir la sobrecarga de la copia de memoria, todos los GC de pausa de transferencia no comprimen ni organizan objetos gigantes. Full GC solo organizará los objetos enormes en su lugar.

Dado que cada colección StartsHumongous y ContinuesHumongous contiene solo un objeto Humongous, parte del último espacio dentro de la colección siempre se desperdicia.
Si el espacio ocupado por un objeto es solo un poco más grande que N regiones, entonces la parte no utilizada del espacio en realidad genera fragmentación de la memoria.

Si en el registro de GC ve una gran cantidad de ciclos concurrentes activados por la asignación enorme y se forma una gran cantidad de fragmentos de memoria en la generación anterior, debe aumentar el valor para que los objetos enormes anteriores ya no se consideren como gigantes -XX:G1HeapRegionSizeEn su lugar, siga el método de asignación de objetos convencional [siempre que sea menos del 50% de la región].

10. Resumen

G1 es un recolector de basura [incremental] en modo [paralelo + concurrente], que divide la memoria del montón en muchas regiones y, en comparación con otras implementaciones de algoritmos de GC, proporciona tiempos de pausa más predecibles y precisos.
La característica incremental permite a G1 manejar espacios de memoria dinámica más grandes y al mismo tiempo mantener tiempos de respuesta razonables en el peor de los casos.

G1 tiene características autoadaptativas. En general, sólo es necesario configurar tres parámetros de ajuste:

  • El tiempo de pausa máximo deseado, p.-XX:MaxGCPauseMillis=50
  • El tamaño máximo de la memoria del montón, por ejemplo.-Xmx4g
  • El valor mínimo de la memoria del montón, por ejemplo.-Xms4g

Sobre el Autor

Monica Beckwith, miembro principal del grupo de trabajo técnico de Oracle, es la líder de rendimiento de Garbage First Garbage Collector en el proyecto Java HotSpot VM.
Más de 10 años de experiencia laboral en el campo de la performance y la arquitectura.
Antes de Oracle y Sun Microsystems, Monica fue responsable del ajuste del rendimiento para Spansion Inc.
Monica ha colaborado con muchos puntos de referencia basados ​​en Java para encontrar mejoras de rendimiento para Java HotSpot VM.

Recursos y enlaces relacionados

Supongo que te gusta

Origin blog.csdn.net/renfufei/article/details/108476781
Recomendado
Clasificación