2009-08-10 21 views
13

Experimentamos demoras de varios minutos en nuestro servidor. Probablemente sean desencadenados por colecciones de basura "detener el mundo". Pero usamos marcas simultáneas y barridos GC (-XX: + UseConcMarkSweepG) así que, creo, estas pausas se desencadenan por la fragmentación de la memoria de la generación anterior.Cómo analizar la fragmentación de memoria en Java?

¿Cómo se puede analizar la fragmentación de memoria de la generación anterior? ¿Hay alguna herramienta para eso?

Los retrasos ocurren cada hora. La mayoría del tiempo son aproximadamente 20 segundos, pero a veces, varios minutos.

+1

¿Estás seguro de que es GC? Especialmente, ¿el proceso vm muestra una alta utilización de CPU durante las pausas? Si no, asumiría que tienes una condición de carrera en alguna parte. – Esko

+0

No es una condición de carrera, estoy seguro. Checked in profiler, y casi no usamos bloqueos, y el comportamiento es el mismo en diferentes servidores, solo diferentes tiempos de retardo. Y no una alta utilización de la CPU: todos los hilos en la aplicación se detienen. – Vitaly

+0

[Editado] Después de activar los registros del GC, descubrí un problema de "promoción fallida". Una buena descripción está aquí: http://www.sun.com/bigadmin/content/submitted/cms_gc_logs.html. Gracias a everybode por ayuda. – Vitaly

Respuesta

6

Consulte la documentación de Java para las opciones "java -X ..." para activar el registro de GC. Eso le dirá si está recolectando generaciones antiguas o nuevas, y cuánto tiempo están tomando las colecciones.

Una pausa de "varios minutos" suena extraordinaria. ¿Estás seguro de que no solo estás ejecutando con un tamaño de pila que es demasiado pequeño, o en una máquina con memoria física insuficiente?

  • Si su montón demasiado cerca de lleno, la GC se disparará una y otra vez , lo que resulta en su servidor pasar la mayor parte de su tiempo de CPU en el GC. Esto se mostrará en los registros de GC .

  • Si se utiliza un gran montón en una máquina con suficiente memoria física, un GC completo es susceptible de causar su máquina de "thrash", el gasto mayor parte de su tiempo locamente movimiento virtuales páginas de memoria a y del disco. Usted puede observar esto usando las herramientas de monitoreo del sistema ; p.ej. viendo la salida de la consola de "vmstat 5" en un sistema UNIX/Linux típico.

FOLLOWUP

Contrariamente a la creencia de la OP, activando el registro GC es poco probable a hacer una diferencia notable con el rendimiento.

La página Understanding Concurrent Mark Sweep Garbage Collector Logs en el sitio de Oracle debería ser útil para interpretar los registros de GC.

Finalmente, la conclusión del OP de que se trata de un problema de "fragmentación" es poco probable, y (IMO) no está respaldada por los fragmentos de evidencia que ha proporcionado. Es muy probable que sea otra cosa.

+0

Varias demoras de minutos pasan, una o dos veces al día. Editado un qustion. – Vitaly

+0

Probaré vmstat 5, gracias – Vitaly

+0

Sí, intentaré la salida de GC detallada. Simplemente imprime demasiada información, puede ralentizar los servidores, no quiere hacerlo :) Ahora usamos GarbageCollectorMXBeans. El resultado es el siguiente: ConcurrentMarkSweep 27459. Y el retraso casi coincide con él (27 segundos). Sucede cada hora más o menos, por eso pienso en la fragmentación de la memoria, no en la pérdida de memoria.- Vitaly 0 segundos atrás [eliminar este comentario] – Vitaly

0

He usado YourKit con buen efecto para este tipo de problema.

+1

Sí, una gran herramienta. Pero no muestra la fragmentación de la memoria, solo el consumo de memoria, que no ayuda con los rezagos. O no sé algunas opciones geniales :)? – Vitaly

+0

YourKit y otros perfiladores de memoria le mostrarán cuándo y con qué frecuencia GC está sucediendo para intentar reorganizar la memoria y reducir la fragmentación. No le mostrará su fragmentación directamente (a menos que yo tampoco conozca una buena opción) –

+0

Voy a verificar el comportamiento de GC a través de VisualVM. Por cierto, YourKit es peligroso ejecutar en servidores de producción. Tuvimos problemas de rendimiento incluso con la opción "desactivar todo". Y gracias por los consejos sobre objetos grandes. – Vitaly

-4

No hay fragmentación de memoria en Java; durante la ejecución del GC, las áreas de memoria se compactan.

Dado que no se ve una alta utilización de CPU, tampoco hay GC funcionando. Entonces, algo más debe ser la causa de tus problemas.Aquí están algunas ideas:

  • Si la base de datos de la aplicación está en un servidor diferente, puede haber problemas en la red

  • Si ejecuta Windows y que haya asignado unidades de red, una de las unidades puede bloquear su computadora (nuevamente problemas de red). Lo mismo es cierto para las unidades NFS en Unix. Verifique el registro del sistema para detectar errores de red.

  • ¿La computadora intercambia muchos datos en el disco? Como la utilización de la CPU es baja, la causa del problema podría ser que la aplicación se cambió a un disco y la ejecución del GC lo obligó a volver a la memoria RAM. Esto llevará mucho tiempo si su servidor no tiene suficiente RAM real para mantener toda la aplicación Java en la RAM.

Además, otros procesos pueden forzar la aplicación de RAM. Compruebe la utilización de la memoria real y el uso de espacio de intercambio.

Para comprender la salida del registro del GC, this post podría ayudar.

[EDITAR] Todavía no puedo entender "baja CPU" y "pérdida de GC". Esos dos generalmente se contradicen entre sí. Si el GC se está estancando, debe ver el uso de la CPU al 100%. Si la CPU está inactiva, entonces algo más está bloqueando el GC. ¿Tiene objetos que sobrecargan finalize()? Si finaliza los bloques, el GC puede demorar una eternidad.

+0

Bueno, hay fragmentación, pero el GC intentará reducirla cuando se ejecute. Tener demasiados objetos de gran tamaño (en relación con el montón disponible) que se asignan/desasignan con frecuencia provocará que la aplicación pase mucho tiempo en GC y dañe el rendimiento. –

+0

Hay una fragmentación de memoria si se usa ConcurrentMarkAndSweep. Por ejemplo, http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.base.doc/info/aes/ae/rprf_javamemory.html. – Vitaly

+0

No se utilizó ninguna base de datos. – Vitaly

0

Vitaly, hay un problema de fragmentación. Mi observación: Si hay objetos pequeños que se actualizan con frecuencia, entonces en ese caso genera mucha basura. Aunque CMS recopila la memoria ocupada por estos objetos, esta memoria está fragmentada. Ahora el hilo Mark-Sweep-Compact entra en escena (detén el mundo) y trata de compactar esta memoria fragmentada causando una pausa prolongada.

Frente a eso, si el tamaño de los objetos es mayor, genera menos memoria fragmentada y
Mark-Swap-Compact toma menos tiempo para compactar esta memoria. Esto puede causar un menor rendimiento pero le ayudará a reducir la pausa prolongada causada por la compactación del GC.

+0

Ya solucionamos el problema. A veces no había suficiente memoria en oldgen para copiar los objetos sobrevividos de la generación joven. Al iniciar el CMS cuando se consumió la memoria de cantidad fija, se solucionó el problema. – Vitaly

+0

Vitaly, ¿Podría indicarnos brevemente la forma en que resolvió el problema de la fragmentación? ¿Cómo desencadenó exactamente el CMS después de consumir una cantidad fija de dinero? ¿Y cómo esto soluciona el problema de la fragmentación? Kishor –

+1

Lea el dinero == memory –

3

Para un control de bajo nivel, querrá usar este -XX:PrintFLSStatistics=1 (o hacerlo 2 por más con un mayor costo de bloqueo). No está documentado y ocasionalmente te da algunas estadísticas. Desafortunadamente no es muy útil en la mayoría de las aplicaciones por diferentes razones, pero al menos es útil.

Usted debe ser capaz de ver, por ejemplo,

Max Chunk Size: 215599441 

y compararlo con este

Total Free Space: 219955840 

y luego juzgar la fragmentación en base a las dimensiones medias de bloque y número de bloques.

0

Esto es un problema difícil de encontrar.Ya que tenía pasar algún tiempo en un sistema de averiguar esto y demostrar, Permítanme enumerar el escenario en el que esto ocurrió

  • Nos quedamos atrapados con el uso de Java 6, que no tiene ningún colector de basura compactación
  • Nuestro solicitud fue haciendo demasiado GC en su mayoría colección generación joven y algunos collecition grande y vieja generación
  • nuestra montón de tamaño era bastante grande- problema principal (hemos reducido, pero nuestra solicitud fue alto consumo de demasiadas cuerdas y colecciones)

El problema que se manifestó w como que solo un algoritmo particular en nuestro sistema estaba funcionando lento; el resto, que funcionaba al mismo tiempo, funcionaba normalmente. Esto descarta el GC completo; También utilizamos jstat y otras herramientas j ** para verificar GC, volcados de hilo + seguimiento de los registros del GC.

Desde los volcados de subprocesos de jstack, tomados durante un tiempo, pudimos obtener una idea de qué bloque de código realmente se estaba desacelerando. Entonces la duda recayó en la fragmentación del montón.

Para probar que escribí un programa simple que inicializó dos List one ArrayList y una LinkedList y agregué operaciones que causan el cambio de tamaño. Esta prueba podría ejecutarse a través del controlador REST. Normalmente no hay mucha diferencia. Pero dentro de un montón fragmentado hay una clara diferencia en el tiempo; un gran tamaño de colección con ArrayList llega a ser muy lento que con la lista Vinculada. Estos tiempos se registraron, y no hubo otra explicación a esto que una cabeza fragmentada.

Con Java 7, pasamos a G1GC, junto con mucho trabajo en sintonización de GC y mejoras en las aplicaciones; Aquí la compactación de montones es mucho mejor y puede manejar montones más grandes, aunque supongo que cualquier pila de más de 16 g te llevará a lugares en los que realmente no quieres ir: GC suckage :)

Cuestiones relacionadas