Colección de basura Java G1 en producción

Dado que Java 7 va a utilizar la nueva colección de basura G1 de forma predeterminada, ¿Java podrá manejar un orden de magnitud mayor sin los supuestos “devastadores” tiempos de pausa del GC? ¿Alguien realmente ha implementado G1 en producción? ¿Cuáles fueron sus experiencias?

Para ser justos, la única vez que he visto pausas de GC realmente largas es en montones muy grandes, mucho más de lo que tendría una estación de trabajo. Para aclarar mi pregunta; ¿G1 abrirá la puerta de entrada a montones en cientos de GB? ¿TUBERCULOSIS?

Parece que el punto de G1 es tener tiempos de pausa más pequeños, incluso hasta el punto en que tiene la capacidad de especificar un objective de tiempo de pausa máximo.

La recolección de basura no es solo un simple “Oye, está lleno, muévete todo de una vez y vuelve a empezar”, trata más: es un sistema increíblemente complejo, de múltiples niveles y con un fondo de rosca. Puede hacer gran parte de su mantenimiento en segundo plano sin pausas, y también utiliza el conocimiento de los patrones esperados del sistema en tiempo de ejecución para ayudar, como suponer que la mayoría de los objetos mueren inmediatamente después de su creación, etc.

Yo diría que los tiempos de pausa de la GC continuarán mejorando, no empeorando, con lanzamientos futuros.

EDITAR:

Al volver a leer, se me ocurrió que uso Java diariamente: Eclipse, Azureus y las aplicaciones que desarrollo, y ha sido MUCHO TIEMPO desde que vi una pausa. No es una pausa significativa, pero me refiero a cualquier pausa en absoluto.

He visto pausas cuando hago clic con el botón derecho en Windows Explorer o (ocasionalmente) cuando conecto cierto hardware USB, pero con Java — none en absoluto.

¿La GC sigue siendo un problema con alguien?

Lo he estado probando con una gran aplicación: 60-70 GB asignados a Heap, con 20-50GB en uso en cualquier momento. Con este tipo de aplicaciones, es un eufemismo decir que su kilometraje puede variar. Estoy ejecutando JDK 1.6_22 en Linux. Las versiones menores son importantes. Antes de aproximadamente 1.6_20, había errores en G1 que causaban NullPointerExceptions al azar.

Descubrí que es muy bueno para mantenerse dentro del objective de pausa que le das la mayor parte del tiempo. El valor predeterminado parece ser una pausa de 100 ms (0.1 segundos), y le he estado diciendo que haga la mitad de eso (-XX: MaxGCPauseMillis = 50). Sin embargo, una vez que tiene muy poca memoria, entra en pánico y realiza una recolección completa de basura mundial. Con 65GB, eso toma entre 30 segundos y 2 minutos. (El número de CPU probablemente no hace la diferencia, probablemente esté limitado por la velocidad del bus).

En comparación con CMS (que no es el servidor GC predeterminado, pero debe ser para servidores web y otras aplicaciones en tiempo real), las pausas típicas son mucho más predecibles y pueden hacerse mucho más cortas. Hasta ahora estoy teniendo mejor suerte con CMS por las largas pausas, pero eso puede ser aleatorio; Los veo solo unas pocas veces cada 24 horas. No estoy seguro de cuál será más apropiado en mi entorno de producción en este momento, pero probablemente G1. Si Oracle sigue sintonizándolo, sospecho que G1 finalmente será el claro ganador.

Si no tiene problemas con los recolectores de basura existentes, no hay ninguna razón para considerar G1 en este momento. Si está ejecutando una aplicación de baja latencia, como una aplicación GUI, G1 es probablemente la opción correcta, con MaxGCPauseMillis establecido realmente bajo. Si está ejecutando una aplicación de modo por lotes, G1 no le compra nada.

Aunque no he probado G1 en producción, pensé que comentaría que los GC ya son problemáticos para los casos sin montones “gigantescos”. Específicamente, los servicios con solo, por ejemplo, 2 o 4 gigas pueden verse gravemente afectados por GC. Los GC de generaciones jóvenes generalmente no son problemáticos ya que terminan en milisegundos de un dígito (o como máximo de dos dígitos). Pero las colecciones de la vieja generación son mucho más problemáticas ya que demoran varios segundos con tamaños de generación anterior de 1 gig o más.

Ahora: en teoría, CMS puede ayudar mucho allí, ya que puede ejecutar la mayor parte de su operación al mismo tiempo. Sin embargo, con el tiempo habrá casos en los que no podrá hacerlo y tendrá que recurrir a la colección “detener el mundo”. Y cuando eso sucede (después de, digamos, 1 hora, no a menudo, pero todavía con demasiada frecuencia), bien, agárrense sus gorros. Puede tomar un minuto o más. Esto es especialmente problemático para los servicios que intentan limitar la latencia máxima; en lugar de tomar, digamos, 25 milisegundos para atender una solicitud, ahora demora diez segundos o más. Para agregar daño a los insultos, los clientes a menudo cortan la solicitud y vuelven a intentarlo, lo que genera más problemas (también conocido como “tormenta de mierda”).

Esta es un área donde se esperaba que G1 ayudara mucho. Trabajé para una gran empresa que ofrece servicios en la nube para almacenamiento y envío de mensajes; y no pudimos usar CMS ya que aunque la mayor parte del tiempo funcionó mejor que las variedades paralelas, tuvo estas fusiones. Así que por alrededor de una hora las cosas fueron agradables; y luego las cosas golpean al ventilador … y como el servicio se basaba en clusters, cuando un nodo se metía en problemas, otros seguían normalmente (dado que los tiempos de espera inducidos por GC llevaban a otros nodos a creer que el nodo se había bloqueado, lo que provocó nuevas rutas).

No creo que GC sea un gran problema para las aplicaciones, y quizás incluso los servicios no agrupados se vean menos afectados. Pero cada vez más sistemas se agrupan (especialmente gracias a los almacenes de datos NoSQL) y los tamaños de almacenamiento dynamic están creciendo. Los GC de OldGen están relacionados de forma super-lineal con el tamaño del montón (lo que significa que duplicar el tamaño del montón más que duplica el tiempo del GC, suponiendo que el tamaño del conjunto de datos en vivo también se duplica).

Azul CTO, Gil Tene, tiene una visión general de los problemas asociados con Garbage Collection y una revisión de varias soluciones en su comprensión de la recolección de basura de Java y lo que puede hacer sobre la presentación, y hay detalles adicionales en este artículo: http: // http://www.infoq.com/articles/azul_gc_in_detail .

El colector de basura C4 de Azul en nuestra JVM de Zing es paralelo y concurrente, y utiliza el mismo mecanismo GC para las generaciones nuevas y antiguas, trabajando simultáneamente y compactando en ambos casos. Lo que es más importante, C4 no tiene una caída en el mundo. Toda la compactación se realiza al mismo tiempo que la aplicación en ejecución. Tenemos clientes que corren muy grandes (cientos de GBytes) con peores tiempos de pausa GC de <10 mseg, y dependiendo de la aplicación muchas veces menos de 1-2 mseg.

El problema con CMS y G1 es que en algún punto la memoria de stack Java debe compactarse, y ambos recolectores de basura dejen el mundo / STW (es decir, pausar la aplicación) para realizar la compactación. Entonces, aunque CMS y G1 pueden expulsar las pausas de STW, no las eliminan. El C4 de Azul, sin embargo, elimina completamente las pausas de STW y es por eso que Zing tiene pausas de GC tan bajas incluso para tamaños de montón gigantescos.

Y para corregir una statement hecha en una respuesta anterior, Zing no requiere ningún cambio en el sistema operativo. Se ejecuta como cualquier otra JVM en distros Linux no modificadas.

Ya estamos usando G1GC, desde hace casi dos años. Funciona muy bien en nuestro sistema de procesamiento de transacciones de misión crítica, y demostró ser un gran soporte con un alto rendimiento, bajas pausas, concurrencia y una gestión optimizada de memoria pesada.

Estamos usando las siguientes configuraciones de JVM:

-server -Xms512m -Xmx3076m -XX:NewRatio=50 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -XX:+AggressiveOpts -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=400 -XX:GCPauseIntervalMillis=8000 -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime 

Actualizado

 -d64 -server -Xss4m -Xms1024m -Xmx4096m -XX:NewRatio=50 -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:-DisableExplicitGC -XX:+AggressiveOpts -Xnoclassgc -XX:+UseNUMA -XX:+UseFastAccessorMethods -XX:ReservedCodeCacheSize=48m -XX:+UseStringCache -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=400 -XX:GCPauseIntervalMillis=8000 

El recostackdor G1 reduce el impacto de las colecciones completas. Si tiene una aplicación en la que ya ha reducido la necesidad de colecciones completas, el recostackdor de barrido simultáneo del mapa es igual de bueno y, según mi experiencia, tiene tiempos de recolección menores.

Parece que G1, que está comenzando con JDK7u4, es oficialmente compatible, consulte la RN para JDK7u4 http://www.oracle.com/technetwork/java/javase/7u4-relnotes-1575007.html .

De nuestras pruebas aún para grandes JVM sintonizados CMS todavía actúa mejor que G1, pero creo que crecerá mejor.

CMS puede conducir a un rendimiento degradado lentamente incluso si lo está ejecutando sin acumular objetos permanentes. Esto se debe a la fragmentación de memoria que G1 supuestamente evita.

El mito sobre G1 disponible solo con soporte pagado es solo eso, un mito. Sun y ahora Oracle han aclarado esto en la página de JDK.

Se supone que G1 GC funciona mejor. Pero si establece -XX: MaxGCPauseMillis demasiado agresivamente, la basura se acumulará muy lentamente. Y es por eso que el GC completo se desencadenó en el ejemplo de David Leppik.

Acabo de implementar G1 Garbage Collector en nuestro proyecto Terracotta Big Memory. Al trabajar en diferentes tipos de colectores, G1 nos dio los mejores resultados con menos de 600 ms de tiempo de respuesta.

Puede encontrar los resultados de las pruebas (26 en total) aquí

Espero eso ayude.

Recientemente me han movido de

CMS a G1GC con 4G Heap y 8 core processor en servidores con JDK 1.7.45 .

(Se prefiere JDK 1.8.x G1GC por encima de 1.7, pero debido a algunas limitaciones, tengo que apegarme a la versión 1.7.45)

Configuré los parámetros clave a continuación y mantuve todos los demás parámetros en los valores predeterminados.

 -XX:G1HeapRegionSize=n, XX:MaxGCPauseMillis=m, -XX:ParallelGCThreads=n, -XX:ConcGCThreads=n apart from -Xms and -Xmx 

Si desea ajustar estos parámetros, eche un vistazo a este artículo de Oracle .

Observaciones clave:

  1. El uso de memoria es consistente con G1GC a diferencia de altos y bajos con CMS
  2. El tiempo máximo de pausa del GC es menor en comparación con el CMS
  3. El tiempo que se pasa en la recolección de basura es un poco alto en G1GC en comparación con CMS.
  4. El número de colecciones principales es casi insignificante en comparación con CMS
  5. Número de colecciones menores en extremo superior en comparación con CMS

Pero aún estoy contento de que el tiempo de pausa de Max GC sea menor que el de CMS. He establecido el tiempo de pausa del GC máximo en 1,5 segundos y este valor aún no se ha cruzado.

Pregunta SE relacionada:

Colección de basura y documentación de Java 7 (JDK 7) en G1

Recientemente migré parte de Twicsy a un nuevo servidor con 128 GB de RAM y decidí usar 1.7. Comencé usando la misma configuración de memoria que utilicé con 1.6 (tengo varias instancias ejecutándose haciendo varias cosas, desde 500mb de heap hasta 15GB, y ahora una nueva con 40GB) y eso no funcionó del todo bien . 1.7 parece usar más montón que 1.6, y experimenté muchos problemas en los primeros días. Afortunadamente, tuve mucha RAM para trabajar y aumenté la memoria RAM para la mayoría de mis procesos, pero aún tenía algunos problemas. Mi MO normal era usar un tamaño de stack mínimo muy pequeño de 16 m, incluso con un montón máximo de varios gigabytes, y luego activar el GC incremental. Esto mantuvo las pausas al mínimo. Sin embargo, eso no funciona ahora, y tuve que boost el tamaño mínimo a aproximadamente lo que esperaba usar en promedio en el montón, y eso ha funcionado muy bien. Todavía tengo el GC incremental encendido, pero lo probaré sin él. Ahora no hay pausas, y las cosas parecen correr muy rápido. Entonces, creo que la moraleja de la historia es que no esperes que tu configuración de memoria se traduzca perfectamente de 1.6 a 1.7.

G1 hace que la aplicación sea mucho más ágil: la latencia de la aplicación boostá: la aplicación puede denominarse como “en tiempo real suave”. Esto se hace reemplazando dos tipos de carreras GC (pequeñas y menores y una grande en la generación sostenida) para las pequeñas de igual tamaño.

Para más detalles mira esto: http://geekroom.de/java/java-expertise-g1-fur-java-7/

Estoy trabajando con Java, para Heap pequeño y grande, y la pregunta de GC y GC completo aparece todos los días, ya que las restricciones pueden ser más estrictas que otras: en cierto entorno, 0.1 segundos de GC carroñero o GC completo, mata simplemente la fonctionnalité, y tener una configuración y capacidad fina es importante (CMS, iCMS, otros … el objective está aquí para tener el mejor tiempo de respuesta posible con el tratamiento casi en tiempo real (aquí el tratamiento en tiempo real es a menudo de 25 ms) , así que, básicamente, cualquier mejora en la ergonomía y heurística de GC es bienvenida.

Utilizo G1GC en Java 8 y también con Groovy (también Java 8), y estoy haciendo varios tipos de cargas de trabajo, y en general G1GC funciona así:

  • El uso de memoria es muy bajo, por ejemplo, 100 MB en lugar de 500 MB en comparación con la configuración Java predeterminada

  • El tiempo de respuesta es constante y muy bajo

  • El rendimiento entre la configuración predeterminada y G1GC es un 20% de desaceleración cuando se usa G1GC en el peor de los casos (sin sintonización, aplicación de subproceso único). No es mucho teniendo en cuenta el buen tiempo de respuesta y el bajo uso de memoria.

  • Cuando se ejecuta desde Tomcat, que tiene múltiples subprocesos, el rendimiento general es un 30% mejor y el uso de la memoria es mucho menor, así como los tiempos de respuesta son mucho menores.

Entonces, en general, cuando se usan cargas de trabajo realmente diversas, G1GC es un buen colector para Java 8 para aplicaciones de subprocesos múltiples, e incluso para un único subproceso hay algunos beneficios.

No se sugiere utilizar java8 w / G1GC para calcular el punto flotante con una JVM tipo punto de acceso. Es peligroso para la integridad y precisión de la aplicación.

https://bugs.openjdk.java.net/browse/JDK-8148175

JDK-8165766

JDK-8186112

    Intereting Posts