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

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

algoritmo de recolección de basura

Descripción del recolector de basura G1

El recolector de basura G1 es un recolector simultáneo que funciona en diferentes particiones en el montón. Una región puede pertenecer a la generación anterior o a la nueva (de forma predeterminada, un montón se divide en 2048 particiones) y las particiones de la misma generación no necesitan ser continuas. La intención original de diseñar particiones para la generación anterior es que cuando los subprocesos en segundo plano simultáneos reciclan objetos a los que no se hace referencia en la generación anterior, algunas particiones tienen una gran cantidad de objetos basura, mientras que otras particiones tienen relativamente pocos objetos basura. Aunque la recolección de elementos no utilizados de las particiones en realidad pausará el subproceso de la aplicación, debido a que el recolector G1 se enfoca en las particiones con la mayor cantidad de elementos no utilizados, el efecto neto es que lleva menos tiempo recolectar elementos no utilizados de estas particiones. Esta forma de centrarse solo en la partición con más basura es el origen del nombre del recolector de basura G1, es decir, la partición con más basura se recoge primero.

Sin embargo, este algoritmo no se aplica a la partición de la nueva generación: cuando la nueva generación realiza la recolección de elementos no utilizados, todo el espacio de la nueva generación se reclama o promueve (los objetos se mueven al espacio Survivor o se mueven a la generación anterior). Parte de la razón por la cual la nueva generación también se particiona es por la facilidad de cambiar el tamaño de la generación con particiones predefinidas.

Las actividades de recolección del colector G1 incluyen principalmente cuatro operaciones:

  • recolección de basura de nueva generación;
  • Colección de antecedentes, ciclo concurrente;
  • recolección de basura mixta;
  • y FullGC cuando sea necesario.

Lo primero que se debe discutir es la recolección de basura de nueva generación del recolector G1, como se muestra en la siguiente figura: inserte la descripción de la imagen aquí
cada pequeño cuadrado en la figura representa una partición G1. El área negra en la partición representa los datos, y la letra en cada partición indica a qué generación pertenece el área ([E] representa el espacio Eden, [0] representa la generación anterior y [S] representa el espacio Survivor). Las particiones vacías no pertenecen a ninguna generación; el recopilador G1 forzará que estas particiones vacías sean utilizadas por cualquier generación deseada cuando sea necesario.

El agotamiento del espacio de Eden activará el recolector de basura G1 para realizar una recolección de basura de nueva generación (en este ejemplo, la recolección de nueva generación se activará después de que se llenen las 4 particiones marcadas como Eden). Después de recopilar la nueva generación, no se asignarán nuevas particiones al espacio Eden inmediatamente, porque el espacio Eden está vacío en este momento. Pero al menos una partición se asignará al espacio Survivor (en este ejemplo, el espacio Survivor está parcialmente lleno) y algunos datos se moverán a la antigüedad. .

En el recolector de basura G1, el registro de la recolección de basura de nueva generación es ligeramente diferente al de otros recolectores. Como de costumbre, puede usar PrintGCDetails para generar los registros de recolección de basura para el ejemplo, pero los registros recopilados por G1 son mucho más detallados. Aquí solo se enumeran las líneas importantes del ejemplo.
El siguiente es el procedimiento estándar para la recolección de basura de nueva generación:

23.430:[GC pause(young),0.23094400 secs]
	[Eden: 1286M(1286M)->0B(1212M)
		Survivors:78M->152M Heap: 1454M(4096M)->242M(4096M)]
	[Times:user=0.85 sys=0.05,real=0.23 secs]

El consumo en tiempo real de la recolección de elementos no utilizados de nueva generación aquí es de 0,23 segundos. Durante este período, el subproceso de recolección de elementos no utilizados consumió 0,85 segundos de tiempo de CPU y se sacaron 1286 MB de objetos del espacio Eden (el tamaño del espacio Eden se ajustó a 1212 MB); 74 MB se movieron al espacio Survivor (el tamaño del espacio Survivor aumentó de 78 MB a 152 MB), y el recolector de basura recuperó el resto del espacio. Este espacio se liberó al observar que la ocupación total del almacenamiento dinámico disminuyó en 1212 MB. Por lo general, algunos objetos se han movido del espacio Survivor al espacio de la generación anterior. Si el espacio Survivor está lleno y no puede acomodar los objetos promovidos de la nueva generación, algunos objetos en el espacio Eden se promoverán directamente al espacio de la generación anterior: en este caso, también aumentará la ocupación del espacio de vejez.
La siguiente imagen muestra el inicio y el final de un ciclo G1 simultáneo: inserte la descripción de la imagen aquí
Hay tres aspectos de esta imagen que vale la pena señalar. Primero, la huella espacial de la generación joven ha cambiado: hay al menos una (y probablemente muchas) recolecciones de basura de generación joven durante un ciclo concurrente. Por lo tanto, antes de que las particiones del espacio Eden se marquen como completamente liberadas, ya se han asignado nuevas particiones Eden.

En segundo lugar, algunas particiones ahora están marcadas con una X. Estas particiones pertenecen a la generación anterior (tenga en cuenta que aún contienen datos) y son las particiones que contienen la mayor cantidad de basura encontrada por el ciclo de marcado.

Finalmente, preste atención también al espacio ocupado por la generación anterior (incluidas las particiones marcadas con O o X), que en realidad puede ser mayor al final del ciclo. Esto se debe a que la recolección de basura de la generación joven promueve objetos a la generación anterior durante el ciclo de marcado. Además de eso, el ciclo de marcado en realidad no libera ningún objeto en la generación anterior: solo bloquea la mayoría de las particiones basura. Los datos basura en estas particiones se reclamarán y liberarán en ciclos posteriores.

El ciclo concurrente del recopilador G1 consta de varias fases, algunas de las cuales suspenden todos los subprocesos de la aplicación y otras no.

La primera fase del ciclo concurrente es la fase de marca inicial . Esta fase suspende todos los subprocesos de la aplicación, en parte porque la fase de marca inicial también realiza la recolección de elementos no utilizados de generación joven.

50.541:[GC pause(young)(initial-mark),0.27767100 secs]
	[Eden:1220M(1220M)->0B(1220M)
		Survivors: 144M->144M Heap: 3242M(4096M)->2093M(4096M)]
	[Times: user=1.02 sys=0.04,real=0.28 secs]

Al igual que la recolección de elementos no utilizados de la generación joven normal, durante la fase de marcado inicial, el subproceso de la aplicación se suspende (alrededor de 0,28 segundos) y luego se vacía la generación joven (se mueven 71 MB de datos de la generación nueva a la generación anterior). El registro de salida de la fase de marca inicial indica que se inició un ciclo simultáneo en segundo plano. Dado que la fase de marcado inicial también debe suspender todos los subprocesos de la aplicación, el colector G1 reutiliza el ciclo GC de nueva generación para completar esta parte del trabajo. El impacto de agregar la fase de marcado inicial al GC de generación joven fue modesto: la sobrecarga de los ciclos de CPU aumentó en aproximadamente un 20% en comparación con los GC anteriores y, aun así, solo hubo un ligero aumento en los tiempos de pausa (afortunadamente, esto hay ciclos de CPU libres en la máquina para ejecutar subprocesos de recopilación G1 simultáneos; de lo contrario, el tiempo de pausa será mayor).

A continuación, el colector G1 escanea la región raíz:

50.819:[GC concurrent-root-region-scan-start]
51.408:[GC concurrent-root-region-scan-end,0.5890230]

Este proceso tarda 0,58 segundos, pero no es necesario suspender el sitio de la aplicación durante el proceso de escaneo. El recopilador G1 utiliza un subproceso en segundo plano para escanear. Sin embargo, la recolección de basura de generación joven no puede ocurrir durante esta fase, por lo que es muy importante reservar suficientes ciclos de CPU para que se ejecuten los subprocesos en segundo plano. Si el espacio de la generación joven se agota mientras se escanea la partición raíz, la recolección de basura de la generación joven (que suspende todos los subprocesos de la aplicación) debe esperar a que se complete el escaneo raíz. En efecto, esto significa que el tiempo de pausa de la recolección de basura de la generación joven será más largo (mucho más que el tiempo normal). Esta situación se ve así en los registros de GC:

350.994:[GC pause(young)
		351.093:[GC concurrent-root-region-scan-end,0.6100090]
		351.093:[GC concurrent-mark-start],
		0.37559600 secs]

Aquí, la pausa del GC ocurre antes del escaneo de la partición raíz, lo que significa que la pausa del GC continuará esperando, puede ver la salida intercalada en el registro del GC. La marca de tiempo del registro de GC muestra que el subproceso de la aplicación esperó alrededor de 100 milisegundos; esta es la razón por la cual el tiempo de pausa del GC de la generación joven es 100 milisegundos más largo que la duración promedio de otras pausas en el registro. Esta es una señal de que el colector G1 necesita ser sintonizado.

Una vez completada la exploración de la partición raíz, el recopilador G1 entra en la fase de marcado concurrente. Esta etapa se ejecuta completamente en segundo plano y se imprime un registro en el registro del GC cuando la etapa se inicia y cuando se detiene.

111.382:[GC concurrent-mark-start]....
120.905:[GC concurrent-mark-end, 9.5225160 sec]

La fase de marcado concurrente es interrumpible, por lo que la recolección de basura de generación joven puede ocurrir durante esta fase. Inmediatamente después de la fase de marcado está la fase de remarcado y la fase de limpieza normal.

120.910:[GC remark 120.959:
		[GC ref-PRC,0.0000890 secs],0.0718990 secs]
		[Times:user=0.23 sys=0.01,real=0.08 secs]
120.985:[GC cleanup 3510M->3434M(4096M),0.0111040 secs]
		[Times: user=0.04 sys=0.00,real=0.01 secs]

Cada una de estas fases pausa el subproceso de la aplicación, aunque la pausa suele ser breve. A esto le sigue una fase adicional de limpieza concurrente:

120.996:[GC concurrent-cleanup-start]
120.996:[GC concurrent-cleanup-end,0.0004520]

Después de esto, el ciclo normal de G1 termina, al menos se completa la ubicación de la basura. La cantidad de memoria realmente reclamada en la fase de limpieza es muy pequeña.Lo que G1 realmente hace hasta este punto es localizar qué particiones antiguas tienen la mayor cantidad de basura reciclable (es decir, la partición marcada con una X en la figura anterior).

Ahora, G1 realizará una serie de recolección de basura mixta (GC mixta). Estas recolecciones de basura se denominan "híbridas" porque no solo realizan una recolección de basura normal de generación joven, sino que también recuperan algunas particiones marcadas por el subproceso de exploración en segundo plano. El efecto de la recolección de basura mixta se muestra en la figura:
inserte la descripción de la imagen aquí
al igual que el comportamiento habitual de la recolección de basura de nueva generación, el recolector G1 vació el espacio Eden y ajustó el tamaño del espacio Survivor. Además, también se han recuperado las dos particiones marcadas. Se ha demostrado que estas particiones contienen una gran cantidad de objetos basura en análisis anteriores, por lo que la mayoría de ellos se han liberado.

Los datos activos en estas particiones se mueven a otra partición (como mover datos activos de la generación joven a la partición de la generación anterior). Esta es la razón por la que el recolector G1 termina con un montón fragmentado con mucha menos frecuencia que el recolector CMS: con la recolección de basura G1 moviendo objetos de esta manera, en realidad acompaña a la compactación.

Para operaciones mixtas de recolección de elementos no utilizados, consulte los siguientes registros:

79.826:[GC pause (mixed),0.26161600 secs]
	[Eden: 1222M(1222M)->0B(1220M)
		Survivors: 142M->144M Heap: 3200M(4096M)->1964M(4096M)]
	[Times: user=1.01 sys=0.00,real=0.26 secs]

Debe tenerse en cuenta que el uso total reducido del montón no es solo los 1222 MB eliminados por el espacio Eden. La diferencia parece ser muy pequeña (solo 16 MB), pero al mismo tiempo, algunos objetos en el espacio Survivor se promueven a la generación permanente Además, cada recolección de basura mixta solo limpiará parte de la partición de generación anterior de destino. Veremos a continuación lo importante que es asegurarse de que la recolección de basura híbrida borre suficiente memoria para evitar fallas simultáneas en el futuro.

El ciclo de GC híbrido continúa hasta que (casi) todas las particiones marcadas se hayan recolectado, después de lo cual el colector G1 reanuda el ciclo regular de GC de generación joven. Eventualmente, el recolector G1 inicia otro ciclo simultáneo para decidir qué particiones deben liberarse en la próxima recolección de elementos no utilizados.

Al igual que el recolector de CMS, a veces se observa Full GC en los registros de recolección de elementos no utilizados.Estos registros son una señal de que se requieren ajustes adicionales (hay muchas formas específicas, e incluso es posible asignar más espacio de almacenamiento dinámico) para mejorar el rendimiento de la aplicación. Hay principalmente 4 situaciones que desencadenan este tipo de Full GC, que se enumeran a continuación:

Falla en el modo de concurrencia
La recolección de basura G1 inicia el ciclo de marcado, pero la generación anterior se llena antes de que se complete el ciclo, en este caso, el recolector G1 abandonará el ciclo de marcado:

51.408:[GC concurrent-mark-start]
65.473:[Full GC 4095M->1395M(4096M),6.1963770 secs]
[Times:user=7.87 sys=0.00,real=6.20 secs]
71.669:[GC concurrent-mark-abort]

Este error significa que el tamaño del almacenamiento dinámico debería haber aumentado, que el procesamiento en segundo plano del recopilador G1 debería haber comenzado antes o que el ciclo debe ajustarse para que se ejecute más rápido (por ejemplo, aumentar la cantidad de subprocesos de procesamiento en segundo plano).

La promoción falla.
El recopilador G1 completa la fase de marcado e inicia la recolección de basura híbrida para limpiar las particiones en la antigüedad. Sin embargo, el espacio de antigüedad se agotará antes de que la recolección de basura libere suficiente memoria. En los registros de recolección de basura, esto generalmente se ve como un GC híbrido seguido de un GC completo.

2226.224:[GC pause(mixed)
		2226.440:[SoftReference,0 refs, 0.0000060 secs]
		2226.441:[NeakReference,θ refs,0.0000020 secs]
		2226.441:[FinalReference,0 refs, 0.0000010 secs]
		2226.441:[PhantomReference,0 refs,0.0000010 secs]
		2226.441:[JNI Neak Reference, 0.0090030 secs]
			(to-space exhausted),0.2390040 secs]
	[Eden:0.0B(400.0M)->0.0B(400.0M)
		Survivors:0.0B->0.0B Heap:2006.4M(2048.0M)->2006.4M(2048.0M)]
	[Times:user=1.70 sys=0.04,real=0.26 secs]
2226.510:[Full GC(Allocation Failure)
		2227.519:[SoftReference, 4329 refs,0.0005520 secs]
		2227.520:[NeakReference,12646 refs,0.0010510 secs]
		2227.521:[FinalReference, 7538 refs,0.0005660 secs]
		2227.521:[PhantomReference, 168 refs,0.0000120 secs]
		2227.521:[JNI Weak Reference,0.0000020 secs]
				2006M->907M(2048M),4.1615450 secs]
	[Times: user=6.76 sys=0.01,real=4.16 secs]

Esta falla generalmente significa que las recolecciones mixtas deben completar las recolecciones de basura más rápidamente; cada recolección de basura de generación joven necesita procesar más particiones de generación anterior.

Error de evacuación
Cuando se realiza la recolección de basura de la generación joven, no hay suficiente espacio en el espacio Survivor y la generación anterior para acomodar todos los objetos supervivientes. Esta situación generalmente se ve como una generación joven especial en el registro de GC:

60.238:[GC pause(young)(to-space overflow),0.41546900 secs]

Este registro indica que el montón está casi completamente agotado o fragmentado. El colector G1 intentará corregir esta falla, pero se puede esperar que el resultado sea peor: el colector G1 cambiará a Full GC. La forma más fácil de resolver este problema es aumentar el tamaño del almacenamiento dinámico.

Errores de asignación de objetos enormes
Las aplicaciones que asignan objetos muy grandes pueden experimentar otro tipo de GC completo al usar el recopilador G1. Actualmente no hay herramientas que puedan diagnosticar fácilmente este tipo de falla específicamente, especialmente de los registros de recolección de basura estándar. Sin embargo, si se produce un GC completo inexplicable, es probable que el origen sea un problema causado por la asignación de objetos gigantes.

sumario rápido

  1. La recolección de elementos no utilizados G1 consta de varios ciclos (y fases dentro de ciclos concurrentes). Una JVM bien ajustada que ejecuta el recopilador G1 solo debe pasar por ciclos de generación joven, ciclos mixtos y ciclos de GC simultáneos.
  2. La fase concurrente de G1 produce un pequeño número de pausas.
  3. Cuando corresponda, debemos ajustar G1 para evitar que se produzcan ciclos completos de GC.

Supongo que te gusta

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