java garbage-collection concurrency

Cómo reducir el fallo del modo concurrente de java y el exceso de gc



garbage-collection concurrency (4)

En Java, el fallo del modo concurrente significa que el recopilador concurrente no logró liberar suficiente espacio de memoria de la generación permanente y permanente, y tiene que darse por vencido y dejar que gc de Stop-the-world completo. El resultado final podría ser muy caro.

Entiendo este concepto pero nunca tuve una buena comprensión integral de
A) ¿Qué podría causar un fallo de modo concurrente y
B) ¿Cuál es la solución?

Este tipo de incertidumbre me lleva a escribir / depurar código sin muchas sugerencias en mente y, a menudo, tiene que darse una vuelta por esos indicadores de rendimiento de Foo a Bar sin razones particulares, solo hay que intentarlo.

Me gustaría aprender de los desarrolladores aquí, ¿cómo es tu experiencia? Si ha encontrado este problema de rendimiento, ¿cuál fue la causa y cómo lo abordó?

Si tiene recomendaciones de codificación, no sea demasiado general. ¡Gracias!


A veces OOM es bastante rápido y muere, a veces sufre un largo período de tiempo (la última vez fue más de 10 horas).

Me parece que una pérdida de memoria está en la raíz de sus problemas.

Un error de CMS no (como yo lo entiendo) causará un OOM. Más bien, se produce un error de CMS porque la JVM debe realizar demasiadas recopilaciones con demasiada rapidez, y CMS no pudo mantenerse al día. Una situación en la que suceden muchos ciclos de recolección en un período corto es cuando su montón está casi lleno.

El tiempo de GC realmente largo suena raro ... pero es teóricamente posible si tu máquina se moviera horriblemente. Sin embargo, un período prolongado de GC repetidos es bastante plausible si su montón está casi lleno.

Puede configurar el GC para que se rinda cuando el montón es 1) en tamaño máximo y 2) todavía casi lleno después de que se haya completado un GC completo. Intenta hacer esto si aún no lo has hecho. No solucionará sus problemas, pero al menos su JVM obtendrá el OOM rápidamente, lo que permitirá un reinicio y recuperación más rápidos del servicio.

EDITAR - la opción para hacer esto es -XX:GCHeapFreeLimit=nnn donde nnn es un número entre 0 y 100 que da el porcentaje mínimo del montón que debe estar libre después del GC. El valor predeterminado es 2. La opción se enumera en la página titulada "La lista más completa de opciones -XX para Java 6 JVM" . (Allí hay muchas opciones de -XX que no aparecen en la documentación de Sun. Desafortunadamente, la página proporciona algunos detalles sobre lo que realmente hacen las opciones).

Probablemente debería comenzar a ver si su aplicación / aplicación web tiene pérdidas de memoria. Si es así, sus problemas no desaparecerán a menos que se encuentren y reparen esas fugas. A largo plazo, jugar con las opciones de Hotspot GC no solucionará las fugas de memoria.


Citado en "Entendimiento de registros de recolector de basura de barrido de marcas concurrentes"

La falla de modo concurrente puede evitarse aumentando el tamaño de generación de titular o iniciando la colección de CMS en una ocupación de pila menor al establecer CMSInitiatingOccupancyFraction en un valor más bajo

Sin embargo, si realmente hay una pérdida de memoria en su aplicación, simplemente está comprando tiempo.

Si necesita un reinicio y recuperación rápidos y prefiere un enfoque de "morir rápido" sugeriría que no utilice CMS en absoluto. Me quedaría con ''-XX: + UseParallelGC''.

De "Ergonomía del recolector de basura"

El recolector de basura paralelo (UseParallelGC) lanza una excepción de memoria insuficiente si se pasa una cantidad excesiva de tiempo recolectando una pequeña cantidad del montón. Para evitar esta excepción, puede aumentar el tamaño del montón. También puede establecer los parámetros -XX:GCTimeLimit=time-limit y -XX:GCHeapFreeLimit=space-limit


Descubrí que usar -XX:PretenureSizeThreshold=1m para hacer que el objeto ''grande'' vaya de inmediato al espacio establecido, redujo en gran medida mi GC joven y las fallas del modo concurrente, ya que no intenta descargar la cantidad de datos del sobreviviente + 1 joven ( xmn=1536m survivorratio=3 maxTenuringThreashould=5 ) antes de que se pueda completar un ciclo completo de CMS. Sí, mi espacio de sobrevivientes es grande, pero aproximadamente una vez cada 2 días aparece algo en la aplicación que lo necesitará (y ejecutamos 12 servidores de aplicaciones cada día para 1 aplicación).


Lo primero que he aprendido sobre el CMS es que necesita más memoria que los otros recopiladores, alrededor del 25 al 50% más es un buen punto de partida. Esto le ayuda a evitar la fragmentación, ya que CMS no hace ninguna compactación como lo haría la detención de los recolectores del mundo. Segundo, haz cosas que ayuden al recolector de basura; Integer.valueOf en lugar de nuevo Integer, deshacerse de las clases anónimas, asegúrese de que las clases internas no tengan acceso a cosas inaccesibles (privadas en la clase externa) cosas por el estilo. Cuanta menos basura mejor. FindBugs y no ignorar las advertencias ayudarán mucho con esto.

En cuanto a la afinación, he descubierto que necesitas probar varias cosas:

-XX: + UseConcMarkSweepGC

Le dice a JVM que use CMS en la versión tenencia.

Corrija el tamaño de su montón: -Xmx2048m -Xms2048m Esto evita que el GC tenga que hacer cosas como crecer y reducir el montón.

-XX: + UseParNewGC

Utilice la colección paralela en lugar de la serie en la generación joven. Esto acelerará tus colecciones menores, especialmente si tienes una generación joven muy grande configurada. Una generación joven grande es generalmente buena, pero no supera la mitad del tamaño de la generación anterior.

-XX: ParallelCMSThreads = X

establezca la cantidad de subprocesos que CMS utilizará cuando haga cosas que se pueden hacer en paralelo.

-XX: + El comentario de CMSParallelRemarkEnabled es serial por defecto, esto puede acelerarlo.

-XX: + CMSIncrementalMode permite que la aplicación se ejecute más al pegar GC entre fases

-XX: + CMSIncrementalPacing permite a JVM calcular la frecuencia con la que se recopila a lo largo del tiempo

-XX: CMSIncrementalDutyCycleMin = X Cantidad mínima de tiempo dedicado a GC

-XX: CMSIncrementalDutyCycle = X Comienza haciendo GC este% del tiempo

-XX: CMSIncrementalSafetyFactor = X

Descubrí que, en general, puede obtener tiempos de pausa bajos si lo configura de manera que básicamente siempre se está acumulando. Como la mayor parte del trabajo se realiza en paralelo, terminas con pausas predecibles básicamente regulares.

-XX: CMSFullGCsBeforeCompaction = 1

Este es muy importante. Le dice al recolector de CMS que siempre complete la colección antes de que comience una nueva. Sin esto, puedes enfrentarte a la situación en la que tira un montón de trabajo y comienza de nuevo.

-XX: + CMSClassUnloadingEnabled

De manera predeterminada, CMS permitirá que su PermGen crezca hasta que mate su aplicación dentro de unas semanas. Esto detiene eso. Sin embargo, su PermGen solo estaría creciendo si usted utiliza Reflection, o está haciendo un mal uso de String.intern, o está haciendo algo malo con un cargador de clases, o algunas otras cosas.

La relación de supervivencia y la tenencia con la que también se puede jugar, dependiendo de si tiene objetos de vida larga o corta, y la cantidad de objetos que se copian entre los espacios de sobrevivientes con los que puede vivir. Si sabe que todos sus objetos se mantendrán, puede configurar espacios de tamaño cero para sobrevivientes, y todo lo que sobreviva a una colección de gen joven se titulará de inmediato.