La guía definitiva para el rendimiento de Java - Resumen 11

algoritmo de recolección de basura

Descripción del recolector de basura G1

Ajuste del recolector de basura G1

El objetivo principal del ajuste del recolector de elementos no utilizados G1 es evitar fallas de modo concurrentes o fallas de evacuación, lo que conduciría a GC completo una vez que ocurran. El truco para evitar los GC completos también se aplica a las recolecciones de basura frecuentes de generaciones jóvenes que esperan a que se complete el escaneo de la partición raíz. En segundo lugar, el ajuste puede minimizar los tiempos de pausa en el proceso. Los métodos enumerados a continuación pueden evitar el GC completo:

  • Aumente el tamaño del espacio de la generación anterior aumentando el tamaño total del espacio de almacenamiento dinámico o ajustando la proporción entre la generación anterior y la nueva.
  • Aumente la cantidad de subprocesos en segundo plano (suponiendo que haya suficientes recursos de CPU para ejecutar estos subprocesos).
  • La actividad de recolección de elementos no utilizados en segundo plano de G1 se produce con mayor frecuencia.
  • Se realiza más trabajo de recolección de basura en un ciclo mixto de recolección de basura.

Hay muchos ajustes que se pueden hacer aquí, pero uno de los objetivos del ajuste del recolector de basura G1 es mantenerlo lo más simple posible. Para lograr este objetivo, el ajuste más importante del colector G1 se realiza a través de una sola bandera: esta bandera también es consistente con la bandera del colector de rendimiento -XX:MaxGCPauseMillis=N.

Cuando se usa el recolector de elementos no utilizados G1, esta marca tiene un valor predeterminado: 200 milisegundos (esto es diferente del recolector de rendimiento). Si la pausa espacio-temporal (detener el mundo) del colector G1 supera este valor, el colector G1 intentará compensarlo de varias maneras, como ajustando la relación entre la nueva generación y la generación anterior, ajustando el tamaño del montón, y antes Inicie el procesamiento en segundo plano más fácilmente, cambie el umbral de promoción o procese más o menos particiones de generación anterior en ciclos mixtos de recolección de elementos no utilizados (esta es la forma más importante).

La compensación habitual ocurre aquí: si disminuye el valor del parámetro, el tamaño de la generación joven se reducirá en consecuencia para cumplir con el objetivo de tiempo de pausa, pero la frecuencia de la recolección de elementos no utilizados de la generación joven será más frecuente. Además, para cumplir con el objetivo de tiempo de pausa, también se reducirá la cantidad de particiones de la generación anterior recopiladas por el GC híbrido, lo que aumentará la posibilidad de fallas en el modo simultáneo.

Si la configuración del objetivo de tiempo de pausa no puede evitar el GC completo, puede ajustar aún más los diferentes aspectos uno por uno. Para el recolector de basura G1, el método de ajuste del tamaño del montón no es diferente de otros algoritmos de recolección de basura.

  1. Ajuste la cantidad de subprocesos de fondo para la recolección de basura G1

Puede intentar aumentar la cantidad de subprocesos de marcado de fondo (suponiendo que la máquina tenga suficiente CPU libre para admitir la ejecución de estos subprocesos). El método de ajuste del subproceso de recolección de elementos no utilizados G1 es similar al método de ajuste del subproceso de recolección de elementos no utilizados de CMS: durante el período en que se suspende el subproceso de la aplicación, puede usar el indicador ParallelGCThreads para establecer el número de subprocesos en ejecución; fase, puede utilizar el indicador ConcGCThreads para establecer el número de subprocesos en ejecución. Sin embargo, el valor predeterminado del indicador ConcGCThreads es diferente en el recopilador G1 que en el recopilador CMS. Se calcula de la siguiente manera:

ConcGCThreads = (ParallelGCThreads + 2)/ 4³

El algoritmo todavía se basa en números enteros; el método de cálculo del colector G1 es casi el mismo que el del colector CMS.

  1. Ajuste la frecuencia con la que se ejecuta el recolector de basura G1

El objetivo de ajuste también se puede lograr si el recolector G1 inicia la recolección de elementos no utilizados antes. El ciclo de recolección de elementos no utilizados G1 generalmente se inicia cuando la ocupación del almacenamiento dinámico alcanza -XX:InitiatingHeapoccupancyPercent=Nla proporción establecida por el parámetro, que es 45 de forma predeterminada. Tenga en cuenta que, a diferencia del recopilador de CMS, el valor de este parámetro se basa en el uso de todo el almacenamiento dinámico, no solo en la generación anterior.

El valor de InitiatingHeapoccupancyPercent es una constante, y el propio recopilador G1 no modificará este valor de parámetro para lograr el objetivo de tiempo de pausa. Si este parámetro se establece en un valor demasiado alto, la aplicación se atascará en GC completo, porque las fases simultáneas no tienen tiempo suficiente para completar la recolección de elementos no utilizados antes de que se llene el espacio de almacenamiento dinámico restante. Si el valor se establece demasiado pequeño, la aplicación realizará una gran cantidad de procesamiento en segundo plano a un ritmo superior al que realmente necesita. Como se discutió en la introducción al recopilador de CMS, debe haber ciclos de CPU disponibles para admitir el procesamiento en segundo plano, por lo que consumir CPU adicional no es tan importante. Sin embargo, esto puede tener consecuencias muy graves, ya que cada vez más subprocesos de aplicación de corta duración se estancan durante la fase concurrente. Estas pausas pueden acumularse rápidamente, así que evite limpiezas de fondo frecuentes cuando utilice el recopilador G1. Una vez que finaliza el ciclo de simultaneidad, compruebe el tamaño del montón para asegurarse de que el valor de InitiatingHeapOccupancyPercent sea mayor que el tamaño del montón en este momento.

  1. Ajustar el ciclo mixto de recolección de basura del recolector G1

Después del ciclo concurrente, el recaudador G1 no puede iniciar un nuevo ciclo concurrente antes de que se complete el cobro de la partición marcada en la vejez. Por lo tanto, otra forma de hacer que el colector G1 comience antes el ciclo de marcado es procesar tantas particiones como sea posible en el ciclo de GC híbrido (para que el ciclo de GC híbrido final sea menor).

La cantidad de trabajo que debe procesar la recolección de elementos no utilizados mixtos depende de tres factores. El primer factor es cuántas particiones se encuentran en su mayoría como objetos basura. Actualmente no hay ningún indicador que pueda ajustar directamente este factor: en la recolección de basura mixta, si el índice de ocupación de basura de la partición alcanza el 35 %, la partición se marca como recolección de basura. (Este factor también puede ser ajustable en algún momento en el futuro, ya existe un -XX:G1MixedGCLiveThresholdPercent=Nparámetro llamado .

El segundo factor es la cantidad máxima de ciclos de GC híbrido cuando G1 recolecta basura y reclama particiones, que -XX:G1MixedGCCountTarget=Nse puede ajustar a través de parámetros. El valor predeterminado de este parámetro es 8; reducir el valor de este parámetro puede ayudar a resolver el problema de falla de promoción (a expensas de tiempos de pausa más largos para ciclos de GC híbridos). Por otro lado, si el tiempo de pausa del GC híbrido es demasiado largo, puede aumentar el valor de este parámetro para reducir la carga de trabajo de cada ciclo del GC híbrido. Sin embargo, antes de ajustar, debemos asegurarnos de que aumentar el valor no provocará demasiado retraso en el próximo ciclo de concurrencia G1, de lo contrario, puede causar que el modo concurrente falle.

Finalmente, el tercer factor que influye es la duración máxima tolerable de las pausas del GC (establecida por el parámetro MaxGCPauseMillis). La duración del ciclo mixto establecida por la bandera MaxGCPauseMillis es regular ascendente Si el tiempo de pausa real está dentro de la duración máxima de la pausa, el colector G1 puede recolectar más de una octava parte de la partición de antigüedad marcada (u otros valores establecidos). El aumento de MaxGCPauseMillis puede recopilar más particiones de la generación anterior en cada GC mixto, lo que a su vez puede ayudar al recopilador G1 a iniciar ciclos simultáneos antes.

sumario rápido

  1. Como primer paso para ajustar el colector G1, se debe establecer como objetivo un tiempo de pausa razonable.
  2. Si Full GC ocurre con frecuencia después de usar esta configuración, y no es probable que el tamaño del montón se expanda, entonces se debe ajustar un método específico para fallas específicas.
  3. El recopilador G1 se puede ajustar mediante el indicador InitiatingHeapOccupancyPercent para iniciar el subproceso de recolección de elementos no utilizados en segundo plano con más frecuencia.
  4. Si tiene suficientes recursos de CPU, puede considerar ajustar el indicador ConcGCThreads para aumentar la cantidad de subprocesos de recolección de elementos no utilizados.
    c. Reduzca el parámetro G1MixedGCCountTarget para evitar fallas en la promoción.

sintonización avanzada

Promoción y Espacio Superviviente

Durante la recolección de elementos no utilizados de la nueva generación, algunos objetos aún pueden estar en el período activo. Algunos de estos objetos son objetos nuevos que se acaban de crear, y estos objetos vivirán durante mucho tiempo, y algunos tienen solo un ciclo de vida corto. Tomemos, por ejemplo, el ciclo que calcula BigDecimals discutido en §. Si la JVM inicia la recolección de basura en la mitad del ciclo, la situación a la que se enfrentan estos objetos BigDecimal de vida muy corta se vuelve muy embarazosa: se acaban de crear, por lo que no se pueden reciclar; son muy cortos, no pueden cumplir las condiciones para promoción a la vieja generación.

Es por eso que la nueva generación se divide en un espacio Eden y dos espacios Survivor. Este diseño permite que los objetos tengan más oportunidades de ser reciclados en la generación joven, y ya no se limita a la promoción a la generación anterior (que eventualmente llena a la generación anterior) .

Durante la recolección de basura de nueva generación, si la JVM encuentra que el objeto aún está muy activo, primero intentará moverlo al espacio Survivor en lugar de moverlo directamente a la generación anterior. Durante la recolección de basura de la primera generación joven, los objetos se mueven del espacio Eden al espacio Survivor 0. En la próxima recolección de elementos no utilizados, los objetos activos se moverán del espacio de Superviviente 0 y el espacio de Eden al espacio de Superviviente 1. Después de eso, el espacio Eden y el espacio Survivor 0 se vacían por completo. La próxima recolección de elementos no utilizados moverá los objetos activos del espacio Survivor 1 y el espacio Eden de regreso al espacio Survivor 0, y así sucesivamente. (El espacio de supervivencia también se denomina espacio "Hasta" y espacio "Desde"; cada vez que se recicla, los objetos se mueven fuera del espacio "Desde" y se mueven al espacio "Hasta". "Desde" y "Hasta" simplemente representan dos Survivor Pointing entre espacios, cada vez que la recolección de basura, la dirección se invertirá).

Obviamente, esta situación no puede durar para siempre, de lo contrario ningún objeto entrará en la vejez. En ambos casos, los objetos se trasladan a la generación anterior. Primero, el tamaño del espacio Survivor es demasiado pequeño. Durante la recolección de basura en la nueva generación, si el espacio de Superviviente de destino está lleno, los objetos activos restantes en el espacio Eden ingresarán directamente a la generación anterior. En segundo lugar, hay un límite superior para el número de ciclos de GC que experimenta un objeto en el espacio Survivor, y los objetos que excedan este límite superior también se moverán a la vejez. Este límite superior se denomina umbral de promoción (umbral de permanencia).

Estos factores que afectan la recolección de basura tienen indicadores de ajuste correspondientes. El espacio Survivor es parte del espacio de nueva generación.Al igual que otras áreas en el montón, la JVM puede ajustarlo dinámicamente. El tamaño inicial del espacio Survivor está -XX:InitialSurvivorRatio=Ndeterminado por banderas. Este valor de parámetro se utiliza en la siguiente fórmula:

survivor_space_size = new_size /(initial_survivor_ratio + 2)

El índice de ocupación de espacio inicial de Survivor (initial_survivor_ratio) tiene un valor predeterminado de 8, a partir del cual se puede calcular que cada espacio de Survivor ocupará aproximadamente el 10 % del espacio de nueva generación.

La JVM puede aumentar el tamaño del espacio Survivor hasta su límite máximo, que se puede -XX:MinSurvivorRatio=Nestablecer mediante parámetros. El indicador MinSurvivorRatio se usa en la siguiente fórmula:

maximum_survivor_space_size = new_size /(min_survivor_ratio + 2)

El valor predeterminado de este parámetro es 3, lo que significa que el tamaño máximo del espacio Survivor es el 20 % del espacio de nueva generación. Este valor de parámetro es un denominador. Cuando el valor del denominador es el más pequeño, la capacidad del espacio Survivor es la más grande. De esta manera, el nombre de este parámetro es un poco poco intuitivo.

Para mantener el tamaño del espacio Survivor en un valor fijo, puede usar el parámetro SurvivorRatio, establecerlo en el valor deseado y desactivar el indicador UseAdaptiveSizepolicy (sin embargo, debe tenerse en cuenta que desactivar el ajuste de tamaño adaptable afectará tanto la nueva generación como la vieja generación.Era).

La JVM juzga si aumentar o disminuir el tamaño del espacio Survivor según la ocupación del espacio Survivor después de la recolección de basura (determinada por la proporción definida). De forma predeterminada, el espacio de Survivor se ajusta para garantizar que el 50 % del espacio esté libre después de la recolección de elementos no utilizados. -XX:TargetSurvivorRatio=NEste valor se puede establecer a través de una bandera .

Finalmente, está la cuestión de cuántos ciclos de GC necesita un objeto para moverse hacia y desde el espacio Survivor antes de pasar a la generación anterior. Esta pregunta depende de la configuración del umbral de promoción. La JVM continuará calculando, buscando lo que cree que es el umbral de promoción más apropiado. El umbral de promoción inicial se puede establecer a través de -XX:InitialTenuringThreshold=Nindicadores (el valor predeterminado es 7 para los recopiladores de Rendimiento y G1, y 6 para el recopilador de CMS). La JVM finalmente elegirá un valor adecuado entre 1 y el umbral máximo de promoción (establecido por el indicador -XX:MaxTenuringThreshold=v). Para el recopilador de rendimiento y el recopilador G1, el umbral máximo de promoción predeterminado es 15 y para el recopilador CMS, el umbral máximo de promoción es 6.

¿Qué parámetros se deben utilizar en qué circunstancias? Mirar las estadísticas de promoción puede ayudarnos a tomar una mejor decisión. Use -XX:+PrintTenuringDistributionel indicador para agregar esta información al registro de GC (de manera predeterminada, -XX:+PrintTenuringDistribution es falso).

Al ver el registro de GC, lo más importante es observar si hay una situación en la que el objeto se promueve directamente a la vejez debido al pequeño espacio de Superviviente en el GC menor. Trate de evitar esta situación: si una gran cantidad de objetos a corto plazo eventualmente llenan la generación anterior, causará un GC completo frecuente.

Al usar el recopilador de rendimiento, la única pista de que esto está sucediendo son las siguientes líneas en el registro del GC:

Desired survivor size 39059456 bytes, new threshold 1(max 15)
	[PSYoungGen: 657856K->35712K(660864K)]
	1659879K->1073807K(2059008K),0.0950040 secs]
	[Times: user=0.32 sys=0.00,real=0.09 secs]

En el registro, podemos ver que el tamaño esperado de un espacio de Survivor en el ejemplo es de 39 MB, y el tamaño total de la nueva generación es de 660 MB: JVM calcula que dos espacios de Survivor ocupan aproximadamente el 11 % del espacio de nueva generación. Sin embargo, surge otro problema: si esta parte del espacio es lo suficientemente grande como para evitar el desbordamiento de la nueva generación a la vieja generación. El registro de recolección de basura no puede responder directamente a esta pregunta, pero por el hecho de que la JVM ajusta el umbral de promoción a 1, se puede juzgar que la JVM promoverá directamente la mayoría de los objetos a la generación anterior y, en consecuencia, reducirá el umbral de promoción a 1. Es muy probable que esta aplicación promueva el objeto directamente a la vejez antes de que el espacio Survivor esté completamente lleno.

Al usar el recopilador G1 o el recopilador CMS, puede obtener más información del registro de recolección de elementos no utilizados:

Desired survivor size 35782656 bytes, new threshold 2(max 6)
- age 1: 33291392 bytes,  33291392 total
- age 2:  4098176 bytes,  37389568 total

El espacio de Survivor esperado es muy similar al ejemplo anterior, alrededor de 35 MB, pero puede ver más información, incluido el tamaño de todos los objetos en el espacio de Survivor. Debido a la necesidad de vender 37 MB de datos, el espacio de Survivor se desbordará.

Que esta situación se pueda mejorar mediante el ajuste depende de las características de la propia aplicación. Si los objetos tienen una vida útil prolongada, que abarca varios ciclos de recolección de elementos no utilizados, eventualmente se moverán a la generación anterior sin importar lo que ajuste, en cuyo caso ajustar el espacio de Survivor y el umbral de promoción no ayudará mucho. Sin embargo, si el objeto se va a reciclar después de varios ciclos de GC, la organización racional del espacio Survivor para que se utilice de manera más eficiente puede mejorar el rendimiento del programa hasta cierto punto.

Si (al reducir la tasa de supervivencia) se aumenta el tamaño del espacio Superviviente, la memoria se divide del espacio Eden de la nueva generación al espacio Superviviente. Sin embargo, la asignación de objetos ocurre en el espacio Eden, lo que significa que la cantidad de objetos que se pueden asignar antes de Minor GC será menor. Por lo tanto, no se recomienda este enfoque.

Otra posibilidad es aumentar el tamaño de la generación joven. El efecto de adoptar este enfoque puede ser contraproducente: aunque los objetos se promocionan a la generación anterior con menos frecuencia, el espacio de la generación anterior se vuelve más pequeño y la aplicación puede experimentar GC completa con más frecuencia.

Si el tamaño del almacenamiento dinámico se puede aumentar al mismo tiempo, tanto la generación joven como la anterior pueden obtener más memoria, que es la mejor solución. El procedimiento recomendado es aumentar el tamaño del montón (o al menos aumentar la generación joven), mientras se reduce la tasa de supervivencia. Con este método, el valor aumentado del espacio Survivor será mayor que el del espacio Eden. El número de recolecciones de basura de generación joven en la aplicación es básicamente el mismo que antes del ajuste. Sin embargo, la cantidad de FC completos será menor, porque se promueven menos objetos a la generación anterior (nuevamente, este ajuste es adecuado para aplicaciones en las que la mayoría de los objetos no sobreviven después de algunos ciclos de GC).

Si se ajusta el espacio Survivor y no se produce un desbordamiento, el objeto solo se promoverá a la vejez cuando la cantidad de ciclos de GC experimentados alcance el valor establecido de MaxTenuringThreshold. Puede aumentar el valor de MaxTenuringThreshold para permitir que el objeto permanezca en el espacio Survivor durante más ciclos. Sin embargo, también debe tenerse en cuenta que a medida que aumenta el umbral de promoción, cuanto más tiempo permanezca el objeto en el espacio Survivor, menor será el espacio libre de Survivor en la colección futura de la nueva generación: más probable es que se produzca el desbordamiento del espacio Survivor. , y el objeto será promovido directamente al espacio Superviviente nuevamente.

sumario rápido

  1. La intención original de diseñar el espacio Survivor es permitir que los objetos (especialmente los objetos asignados) permanezcan en la nueva generación durante más ciclos de GC. Este diseño aumenta las posibilidades de que los objetos se recuperen antes de que pasen a la generación anterior.
  2. Si el espacio de Survivor es demasiado pequeño, el objeto se promoverá directamente a la generación anterior, lo que activará más GC de generación anterior.
  3. La mejor manera de resolver este problema es aumentar el tamaño del montón (o al menos aumentar la generación joven) y dejar que la JVM maneje el reciclaje del espacio Survivor.
  4. En algunos casos, es necesario evitar que los objetos se promuevan a la generación anterior. Ajustar el umbral de promoción o el tamaño del espacio Superviviente puede evitar que los objetos se promuevan a la generación anterior.

Supongo que te gusta

Origin blog.csdn.net/weixin_42583701/article/details/131179206
Recomendado
Clasificación