java performance garbage-collection heap

Tamaños de montón muy grandes Java



performance garbage-collection (14)

¿Alguien tiene experiencia con el uso de montones muy grandes, 12 GB o más en Java?

  • ¿El GC hace inutilizable el programa?
  • ¿Qué parámetros de GC usas?
  • ¿Qué JVM, Sun o BEA serían más adecuados para esto?
  • ¿Qué plataforma, Linux o Windows, funciona mejor bajo tales condiciones?
  • En el caso de Windows, ¿hay alguna diferencia de rendimiento entre Vista y XP de 64 bits en cargas de memoria tan altas?

¡Soy el CEO de Azul Systems, así que obviamente soy parcial en mi opinión sobre este tema! :) Habiendo dicho eso...

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://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 pila 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.


12Gb no debería ser un problema con una implementación de JVM decente como el Hotspot de Sun. Le aconsejaría que utilice el colllector Marca simultánea y barrido (-XX: + UseConcMarkSweepGC) cuando utilice una máquina virtual SUN. Otras veces puede enfrentar fases largas de "detener el mundo", donde todos los hilos se detienen durante un GC.

El sistema operativo no debe hacer una gran diferencia para el rendimiento del GC.

Necesitará, por supuesto, un sistema operativo de 64 bits y una máquina con suficiente memoria RAM física.


Como se mencionó anteriormente, si tiene un programa no interactivo, el recolector de basura (GC) predeterminado (compactación) debería funcionar bien. Si tiene un programa interactivo, y (1) no asigna memoria más rápido de lo que el GC puede mantener, y (2) no crea objetos temporales (o colecciones de objetos) que son demasiado grandes (en relación con el total memoria JVM máxima) para que el GC funcione, entonces CMS es para usted.

Te tropiezas con problemas si tienes un programa interactivo donde el GC no tiene suficiente espacio para respirar. Eso es cierto independientemente de la cantidad de memoria que tenga, pero mientras más memoria tenga, peor se pondrá. Esto se debe a que cuando se queda muy bajo en memoria, CMS se quedará sin memoria, mientras que los GC de compactación (incluido G1) pausarán todo hasta que toda la memoria se haya revisado en busca de basura. Esta pausa stop-the-world se hace más grande cuanto más memoria tienes. Créeme, no quieres que tus servlets paren por más de un minuto. Escribí una respuesta detallada de sobre estas pausas en G1.

Desde entonces, mi compañía ha cambiado a Azul Zing. Todavía no puede manejar el caso donde su aplicación realmente necesita más memoria de la que tiene, pero hasta ese momento funciona como un sueño.

Pero, por supuesto, Zing no es gratis y su salsa especial está patentada. Si tiene mucho más tiempo que dinero, intente volver a escribir su aplicación para usar un conjunto de JVM.

En el horizonte, Oracle está trabajando en un GC de alto rendimiento para montones de varios gigabytes. Sin embargo, a partir de hoy esa no es una opción.


Debería intentar ejecutar VisualGc contra su aplicación. Es una herramienta de visualización de gran capacidad que forma parte de la descarga de jvmstat en http://java.sun.com/performance/jvmstat/

Es mucho más fácil que leer los registros de GC.

Rápidamente lo ayuda a comprender cómo funcionan las partes (generaciones) del montón. Mientras que su montón total puede ser de 10 GB, las diversas partes del montón serán mucho más pequeñas. Los GC en la porción Eden del montón son relativamente baratos, mientras que los GC completos en la generación anterior son caros. Dimensionar su montón para que el Edén sea grande y la generación anterior apenas se toque es una buena estrategia. Esto puede dar como resultado un montón general muy grande, pero qué diablos, si la JVM nunca toca la página, es solo una página virtual, y no tiene que ocupar la memoria RAM.


Hace un par de años, comparé JRockit y Sun JVM para un montón de 12G. JRockit ganó, y el soporte de páginas enormes de Linux hizo que nuestra prueba se ejecutara un 20% más rápido. YMMV ya que nuestra prueba era muy intensiva en el uso del procesador / memoria y principalmente tenía un único subproceso.


He utilizado más de 60 GB de tamaño de almacenamiento dinámico en dos aplicaciones diferentes en Linux y Solaris, respectivamente, utilizando versiones de 64 bits (obviamente) de Sun 1.6 JVM.

Nunca me encontré con problemas de recolección de basura con la aplicación basada en Linux, excepto cuando empujaba hacia arriba cerca del límite de tamaño de pila. Para evitar los problemas de agitación inherentes a ese escenario (demasiado tiempo dedicado a la recolección de basura), simplemente optimicé el uso de la memoria en todo el programa, de modo que el uso máximo estuvo entre un 5-10% por debajo del límite de almacenamiento de 64 GB.

Sin embargo, con una aplicación diferente que se ejecutaba en Solaris, encontré problemas significativos de recolección de basura que hicieron que fuera necesario realizar muchos ajustes. Esto consistió principalmente en tres pasos:

  1. Habilitar / forzar el uso del recolector de basura paralelo mediante las opciones de JVM -XX: + UseParallelGC -XX: + UseParallelOldGC, así como controlar el número de subprocesos de GC utilizados mediante la opción -XX: ParallelGCThreads. Consulte " Ajuste de recolección de basura de la máquina virtual Java SE 6 HotSpot " para obtener más detalles.

  2. Configuración extensiva y aparentemente ridícula de variables locales para "nulo" después de que ya no se necesiten. La mayoría de estas fueron variables que deberían haber sido elegibles para la recolección de basura después de salir del alcance, y no fueron situaciones de pérdida de memoria ya que las referencias no se copiaron. Sin embargo, esta estrategia de "mantenimiento manual" para ayudar a la recolección de basura fue inexplicablemente necesaria por alguna razón para esta aplicación bajo la plataforma Solaris en cuestión.

  3. Uso selectivo de la llamada al método System.gc () en las secciones de código clave después de períodos extensos de asignación temporal de objetos. Soy consciente de las advertencias estándar contra el uso de estas llamadas, y el argumento de que normalmente deberían ser innecesarias, pero descubrí que son fundamentales para controlar la recolección de basura cuando se ejecuta esta aplicación que requiere mucha memoria.

Los tres pasos anteriores hacían posible mantener esta aplicación contenida y funcionando productivamente con un uso de aproximadamente 60 GB de almacenamiento dinámico en lugar de crecer fuera de control hasta el límite de almacenamiento de 128 GB que estaba en su lugar. El recolector de basura paralelo en particular fue muy útil ya que los grandes ciclos de recolección de basura son costosos cuando hay muchos objetos, es decir, el tiempo requerido para la recolección de basura mayor es una función del número de objetos en el montón.

No puedo comentar sobre otros problemas específicos de la plataforma en esta escala, ni he utilizado JVM que no sean de Sun (Oracle).


La memoria máxima que XP puede abordar es de 4 gigas ( here ). Por lo que es posible que no desee usar XP para eso (use un sistema operativo de 64 bits).


Si cambia a 64 bits, usará más memoria. Los punteros se convierten en 8 bytes en lugar de 4. Si está creando muchos objetos, esto puede notarse ya que cada objeto es una referencia (puntero).

Recientemente, he asignado 15 GB de memoria en Java utilizando Sun 1.6 JVM sin problemas. Aunque todo se asigna solo una vez. No se asigna o libera mucha más memoria después de la cantidad inicial. Esto fue en un Linux, pero imagino que Sun JVM funcionará igual de bien en Windows de 64 bits.


Si su aplicación no es interactiva, y las pausas de GC no son un problema para usted, no debería haber ningún problema para que Java de 64 bits maneje montones muy grandes, incluso en cientos de GB. Tampoco hemos notado ningún problema de estabilidad en Windows o Linux.

Sin embargo, cuando necesitas mantener bajas las pausas de GC, las cosas se ponen realmente desagradables:

  1. Olvídese del rendimiento predeterminado, stop-the-world GC. Pausará su aplicación durante varias decenas de segundos para montones moderados (<~ 30 GB) y varios minutos para los más grandes (> ~ 30 GB). Y comprar módulos DIMM más rápidos no ayudará.

  2. La mejor apuesta es probablemente el recopilador de CMS, habilitado por -XX: + UseConcMarkSweepGC. El recolector de basura de CMS detiene la aplicación solo para la fase de marcado inicial y las fases de observación. Para montones muy pequeños como <4 GB esto no suele ser un problema, pero para una aplicación que crea mucha basura y un gran montón, la fase de observación puede llevar bastante tiempo, generalmente mucho menos que el stop-the-world. , pero aún puede ser un problema para montones muy grandes.

  3. Cuando el recolector de basura CMS no es lo suficientemente rápido como para terminar la operación antes de que la generación permanente se llene, regresa al GC stop-the-world estándar. Espere ~ 30 o más segundas pausas largas para montones de tamaño 16 GB. Puede tratar de evitar que esto mantenga la tasa de producción de basura de larga vida de su aplicación lo más baja posible. Tenga en cuenta que cuanto mayor sea el número de núcleos que ejecuta su aplicación, mayor será el problema, ya que el CMS utiliza solo un núcleo. Obviamente, tenga en cuenta que no hay garantía de que el CMS no recaiga en el recopilador de STW. Y cuando lo hace, generalmente ocurre en las cargas máximas, y su aplicación está muerta por varios segundos. Probablemente no desee firmar un acuerdo de nivel de servicio para dicha configuración.

  4. Bueno, está esa nueva cosa G1. Teóricamente está diseñado para evitar los problemas con CMS, pero lo hemos probado y hemos observado que:

    • Su rendimiento es peor que el de CMS.
    • Teóricamente, debe evitar la recopilación de los bloques populares de memoria primero, sin embargo, pronto alcanza un estado donde casi todos los bloques son "populares", y las suposiciones en las que se basa simplemente dejan de funcionar.
    • Finalmente, la reserva de parar al mundo todavía existe para G1; pregunte a Oracle, cuando se supone que se debe ejecutar ese código. Si dicen "nunca", pregúnteles por qué el código está allí. Así que IMHO G1 realmente no hace desaparecer el gran problema de montón de Java, solo lo hace (posiblemente) un poco más pequeño.
  5. Si tiene dinero para un gran servidor con gran capacidad de memoria, probablemente también tenga una buena tecnología de CG comercial, apta para hardware acelerada y sin pausa, como la ofrecida por Azul. Tenemos uno de sus servidores con 384 GB de RAM y realmente funciona bien, no hay pausas, 0 líneas de código stop-the-world en el GC.

  6. Escriba la maldita parte de su aplicación que requiere mucha memoria en C ++, como lo hizo LinkedIn con el procesamiento de gráficos sociales. Todavía no evitará todos los problemas haciendo esto (por ejemplo, fragmentación de montón), pero sería definitivamente más fácil mantener las pausas bajas.


También recomiendo considerar hacer un volcado dinámico y ver dónde se puede mejorar el uso de la memoria en tu aplicación y analizar el volcado en algo como el MAT de Eclipse . Hay algunos artículos en la página MAT para comenzar a buscar fugas de memoria. Puede usar jmap para obtener el volcado con algo como ...

jmap -heap:format=b pid


Tenemos una aplicación a la que asignamos 12-16 Gb pero realmente solo llega a 8-10 durante el funcionamiento normal. Usamos Sun JVM (probamos IBM y fue un desastre, pero eso podría haber sido una ignorancia de nuestra parte ... Tengo amigos que lo juran, que funcionan en IBM). Mientras le dé a su aplicación un respiro, la JVM puede manejar grandes tamaños de almacenamiento dinámico sin demasiado GC. Una gran cantidad de memoria "extra" es la clave.
Linux es casi siempre más estable que Windows y cuando no es estable, es mucho más fácil averiguar por qué. Solaris también es sólido y tú también obtienes DTrace :) Con este tipo de cargas, ¿por qué usarías Vista o XP? Solo estás pidiendo problemas. No hacemos nada elegante con los parámetros GC. Establecemos que la asignación mínima sea igual al máximo, por lo que no intentamos cambiar el tamaño constantemente, pero eso es todo.


aquí hay un artículo sobre gc DE uno de los campeones de Java - http://kirk.blog-city.com/is_your_concurrent_collector_failing_you.htm

Kirk, el autor escribe "Envíame tus registros de GC"

Actualmente estoy interesado en estudiar registros de GC producidos por Sun JVM. Dado que estos registros no contienen información comercial relevante, debería ser una preocupación para la protección de la información confidencial. Todo lo que pido es que con el registro mencione el sistema operativo, complete la información de la versión del JRE y cualquier conmutador de línea de comando relacionado con heap / gc que haya establecido. También me gustaría saber si está ejecutando Grails / Groovey, JRuby, Scala o algo que no sea Java. La mejor configuración es -Xloggc :. Tenga en cuenta que este registro no se transfiere cuando alcanza el límite de tamaño de su sistema operativo. Si encuentro algo interesante, estaré encantado de darle una sinopsis muy rápida a cambio. "


sun ha tenido un itanium de 64 bits jvm por un tiempo, aunque itanium no es un destino popular. Las JVM de Solaris y Linux de 64 bits deberían ser lo que usted debería ser.
Algunas preguntas

1) es su aplicación estable?
2) ¿Ya has probado la aplicación en una JVM de 32 bits?
3) ¿está bien ejecutar múltiples JVM en la misma caja?

Esperaría que el sistema operativo de 64 bits de Windows se estabilice en aproximadamente un año más o menos, pero hasta entonces, Solaris / Linux podría ser una mejor apuesta.