Dado que Java 7 va a usar la nueva recolección de basura G1 de forma predeterminada, ¿Java podrá manejar un montón de orden de magnitud mayor sin tiempos de pausa de GC supuestamente "devastadores"? ¿Alguien 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 hubiera hecho una estación de trabajo. Para aclarar mi pregunta; ¿G1 abrirá la puerta de entrada a montones de cientos de GB? ¿TUBERCULOSIS?
java
garbage-collection
java-7
g1gc
benstpierre
fuente
fuente
Respuestas:
Parece que el objetivo de G1 es tener tiempos de pausa más pequeños, incluso hasta el punto en que tiene la capacidad de especificar un objetivo de tiempo de pausa máximo.
La recolección de basura ya no es un simple trato de "Oye, está lleno, movamos todo de una vez y empecemos de nuevo", es un sistema increíblemente complejo, de múltiples niveles y en segundo plano. Puede hacer gran parte de su mantenimiento en segundo plano sin pausas en absoluto, 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 justo después de ser creados, etc.
Yo diría que los tiempos de pausa de GC seguirán mejorando, no empeorando, con futuras versiones.
EDITAR:
Al volver a leer, se me ocurrió que uso Java a diario: Eclipse, Azureus y las aplicaciones que desarrollo, y ha pasado MUCHO TIEMPO desde que vi una pausa. No es una pausa significativa, pero me refiero a cualquier pausa.
He visto pausas cuando hago clic con el botón derecho en el explorador de Windows o (ocasionalmente) cuando conecto cierto hardware USB, pero con Java, ninguna en absoluto.
¿GC sigue siendo un problema para alguien?
fuente
Lo he estado probando con una aplicación pesada: 60-70GB asignados al montón, 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 secundarias son importantes: antes de aproximadamente 1.6_20, había errores en G1 que causaban NullPointerExceptions aleatorias.
Descubrí que es muy bueno para mantenerse dentro del objetivo de pausa que le da 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 poca memoria, entra en pánico y hace una recolección de basura completa. Con 65GB, eso toma entre 30 segundos y 2 minutos. (La cantidad de CPU probablemente no haga una diferencia; probablemente esté limitada por la velocidad del bus).
En comparación con CMS (que no es el servidor GC predeterminado, pero debería serlo para servidores web y otras aplicaciones en tiempo real), las pausas típicas son mucho más predecibles y se pueden hacer mucho más cortas. Hasta ahora estoy teniendo más suerte con CMS por las grandes 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 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 configurado realmente bajo. Si está ejecutando una aplicación en modo por lotes, G1 no le compra nada.
fuente
Aunque no he probado G1 en producción, pensé que comentaría que los GC ya son problemáticos para los casos sin montones "enormes". Específicamente, los servicios con solo, digamos, 2 o 4 gigas pueden verse gravemente afectados por GC. Los GC de generación joven generalmente no son problemáticos ya que terminan en milisegundos de un solo dígito (o como mucho en dos dígitos). Pero las colecciones de la vieja generación son mucho más problemáticas ya que toman varios segundos con tamaños de la vieja generación de 1 giga o más.
Ahora: en teoría, CMS puede ayudar mucho allí, ya que puede ejecutar la mayor parte de su funcionamiento al mismo tiempo. Sin embargo, con el tiempo habrá casos en los que no podrá hacer esto y tendrá que recurrir a la colección "stop the world". Y cuando eso suceda (después de, digamos, 1 hora, no a menudo, pero todavía con demasiada frecuencia), bueno, agárrate a tus malditos sombreros. Puede tardar 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 toma diez segundos o más. Para agregar daño al insulto, los clientes a menudo agotan la solicitud y lo vuelven a intentar, lo que genera más problemas (también conocido como "tormenta de mierda").
Esta es un área en la que 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 funcionaba mejor que las variedades paralelas, tenía estos colapsos. Así que durante una hora las cosas estuvieron bien; y luego las cosas golpearon el ventilador ... y debido a que el servicio se basaba en clústeres, cuando un nodo se metía en problemas, otros generalmente lo seguían (ya que los tiempos de espera inducidos por GC hacen que otros nodos crean que el nodo se ha bloqueado, lo que lleva a redireccionamientos).
No creo que GC sea un gran problema para las aplicaciones, y tal vez incluso los servicios no agrupados se vean afectados con menos frecuencia. Pero cada vez más sistemas están agrupados (especialmente gracias a los almacenes de datos NoSQL) y el tamaño de los montones está creciendo. Los GC de OldGen están superlinealmente relacionados 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, asumiendo que el tamaño del conjunto de datos en vivo también se duplica).
fuente
El CTO de Azul, Gil Tene, tiene una buena descripción general de los problemas asociados con Garbage Collection y una revisión de varias soluciones en su presentación Understanding Java Garbage Collection y What You Can Do al respecto , y hay detalles adicionales en este artículo: http: // www.infoq.com/articles/azul_gc_in_detail .
El recolector de basura C4 de Azul en nuestra JVM de Zing es paralelo y concurrente, y utiliza el mismo mecanismo de GC tanto para las generaciones nuevas como para las antiguas, trabajando simultáneamente y compactando en ambos casos. Lo más importante es que C4 no tiene un retroceso que pare el mundo. Toda la compactación se realiza al mismo tiempo que la aplicación en ejecución. Tenemos clientes que ejecutan grandes cantidades (cientos de GBytes) con, en el peor de los casos, tiempos de pausa de GC de <10 mseg, y dependiendo de la aplicación, a menudo, menos de 1-2 mseg.
El problema con CMS y G1 es que en algún momento la memoria del montón de Java debe compactarse, y ambos recolectores de basura detienen el mundo / STW (es decir, pausan la aplicación) para realizar la compactación. Entonces, aunque CMS y G1 pueden eliminar las pausas STW, no las eliminan. Sin embargo, el C4 de Azul elimina por completo las pausas STW y es por eso que Zing tiene pausas GC tan bajas incluso para tamaños de montón gigantes.
Y para corregir una afirmación hecha en una respuesta anterior, Zing no requiere ningún cambio en el sistema operativo. Se ejecuta como cualquier otra JVM en distribuciones de Linux sin modificar.
fuente
Ya estamos usando G1GC, desde hace casi dos años. Está funcionando muy bien en nuestro sistema de procesamiento de transacciones de misión crítica, y demostró ser un gran soporte para alto rendimiento, bajas pausas, concurrencia y administración optimizada de memoria pesada.
Estamos utilizando la siguiente configuración de JVM:
Actualizado
fuente
El colector G1 reduce el impacto de las colecciones completas. Si tiene una aplicación en la que ya ha reducido la necesidad de recopilaciones completas, el colector de barrido de mapas concurrentes es igual de bueno y, en mi experiencia, tiene tiempos de recopilación menor más cortos.
fuente
Parece que G1 que inicia JDK7u4 finalmente es oficialmente compatible, consulte el RN para JDK7u4 http://www.oracle.com/technetwork/java/javase/7u4-relnotes-1575007.html .
Según nuestras pruebas, aún para grandes JVM, el CMS sintonizado todavía funciona mejor que G1, pero supongo que crecerá mejor.
fuente
Recientemente me han trasladado de
CMS a G1GC con heap 4G y procesador de 8 núcleos en servidores con JDK 1.7.45 .
(Se prefiere JDK 1.8.x G1GC a 1.7 pero debido a algunas limitaciones, tengo que ceñirme a la versión 1.7.45)
He configurado los siguientes parámetros clave y he mantenido todos los demás parámetros en los valores predeterminados.
Si desea ajustar estos parámetros, eche un vistazo a este artículo de Oracle .
Observaciones clave:
Pero todavía estoy feliz de que el tiempo de pausa de Max GC sea menor que el de CMS. Establecí el tiempo de pausa Max GC en 1,5 segundos y este valor aún no se ha cruzado.
Pregunta SE relacionada:
Recolección de basura y documentación de Java 7 (JDK 7) en G1
fuente
CMS puede provocar una degradación lenta del rendimiento incluso si lo ejecuta sin acumular objetos permanentes. Esto se debe a la fragmentación de la memoria que supuestamente G1 evita.
El mito sobre G1 disponible solo con soporte pago es solo eso, un mito. Sun y ahora Oracle han aclarado esto en la página de JDK.
fuente
Se supone que G1 GC funciona mejor. Pero si configura -XX: MaxGCPauseMill es demasiado agresivo, la basura se recolectará demasiado lentamente. Y es por eso que GC completo se activó en el ejemplo de David Leppik.
fuente
Acabo de implementar G1 Garbage Collector en nuestro proyecto Terracotta Big Memory. Mientras trabajaba en diferentes tipos de colectores, G1 nos dio los mejores resultados con un tiempo de respuesta de menos de 600ms.
Puede encontrar los resultados de las pruebas (26 en total) aquí
Espero eso ayude.
fuente
Recientemente he migrado parte de Twicsy a un nuevo servidor con 128 GB de RAM y decidí utilizar 1.7. Comencé usando las mismas configuraciones de memoria que usé con 1.6 (tengo varias instancias ejecutándose haciendo varias cosas, desde 500 MB de pila hasta 15 GB, y ahora una nueva con 40 GB) y eso no funcionó bien en absoluto . 1.7 parece usar más montón que 1.6, y experimenté muchos problemas durante los primeros días. Afortunadamente, tenía mucha RAM para trabajar y aumenté la RAM para la mayoría de mis procesos, pero todavía tenía algunos problemas. Mi MO normal era usar un tamaño de montón mínimo muy pequeño de 16 m, incluso con un montón máximo de varios gigabytes, y luego activar GC incremental. Esto mantuvo las pausas al mínimo. Sin embargo, eso no funciona ahora, y tuve que aumentar 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 activado el GC incremental, pero lo intentaré sin. Ahora no hay pausas en absoluto, y las cosas parecen ir muy rápido. Entonces, creo que la moraleja de la historia es que no espere que la configuración de su memoria se traduzca perfectamente de 1.6 a 1.7.
fuente
G1 hace que la aplicación sea mucho más ágil: aumentará la latencia de la aplicación; la aplicación puede denominarse "soft-real-time". Esto se hace reemplazando dos tipos de ejecuciones de GC (pequeñas y menores y una grande en Tenured Gen) por pequeñas de igual tamaño.
Para obtener más detalles, consulte esto: http://geekroom.de/java/java-expertise-g1-fur-java-7/
fuente
Estoy trabajando con Java, para Heap pequeño y grande, y la cuestión de GC y Full GC aparece todos los días, ya que las restricciones pueden ser más estrictas que otras: en cierto entorno, 0.1 segundos de scavenger GC o Full GC, kill simplemente la funcionalidad, y tener una configuración fina y la capacidad es importante (CMS, iCMS, otros ... el objetivo está aquí para tener el mejor tiempo de respuesta posible con el tratamiento en tiempo casi real (aquí el tratamiento en tiempo real suele ser de 25 ms) , así que, básicamente, ¡cualquier mejora en la ergonomía y heurística de GC es bienvenida!
fuente
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 predeterminada de Java
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 ajuste, aplicación de un solo subproceso). No es mucho considerar un buen tiempo de respuesta y un bajo uso de memoria.
Cuando se ejecuta desde Tomcat, que es multiproceso, el rendimiento general es un 30% mejor y el uso de la memoria es mucho menor y los tiempos de respuesta son mucho menores.
Entonces, en general, cuando se utilizan cargas de trabajo realmente diversas, G1GC es un muy buen recopilador de Java 8 para aplicaciones de múltiples subprocesos, e incluso para las de un solo subproceso hay algunos beneficios.
fuente
No se sugiere utilizar java8 con G1GC para el cálculo de punto flotante con JVM similar a un punto de acceso. Es peligroso para la integridad y precisión de las aplicaciones.
https://bugs.openjdk.java.net/browse/JDK-8148175
JDK-8165766
JDK-8186112
fuente