Rastreando un problema de pérdida de memoria / recolección de basura en Java

Este es un problema que he estado tratando de localizar durante un par de meses. Tengo una aplicación java ejecutando que procesa feeds xml y almacena el resultado en una base de datos. Ha habido problemas de recursos intermitentes que son muy difíciles de rastrear.

Fondo: En la caja de producción (donde el problema es más notable), no tengo un acceso particularmente bueno a la caja, y no he podido ejecutar Jprofiler. Esa caja es una máquina de 8 bits y 64 bits de cuatro núcleos que ejecuta centos 5.2, tomcat6 y java 1.6.0.11. Comienza con estos java-opts.

JAVA_OPTS="-server -Xmx5g -Xms4g -Xss256k -XX:MaxPermSize=256m -XX:+PrintGCDetails -
XX:+PrintGCTimeStamps -XX:+UseConcMarkSweepGC -XX:+PrintTenuringDistribution -XX:+UseParNewGC"

La pila de tecnología es la siguiente:

Centos de 64 bits 5.2Java 6u11Tomcat 6Primavera / WebMVC 2.5Hibernar 3Cuarzo 1.6.1DBCP 1.2.1Mysql 5.0.45Ehcache 1.5.0(y, por supuesto, una gran cantidad de otras dependencias, en particular las bibliotecas de jakarta-commons)

Lo más cercano que puedo llegar a reproducir el problema es una máquina de 32 bits con menores requisitos de memoria. Que tengo control sobre. Lo probé hasta la muerte con JProfiler y solucioné muchos problemas de rendimiento (problemas de sincronización, precompilación / almacenamiento en caché de consultas xpath, reducción del conjunto de subprocesos y eliminación de la precarga de hibernación innecesaria, y "calentamiento de caché" demasiado entusiasta durante el procesamiento).

En cada caso, el generador de perfiles mostró que tomaban enormes cantidades de recursos por una razón u otra, y que estos ya no eran una fuente de recursos primarios una vez que entraron los cambios.

El problema: La JVM parece ignorar completamente la configuración de uso de la memoria, llena toda la memoria y deja de responder. Este es un problema para el cliente, que espera un sondeo regular (5 minutos y un minuto de reintento), así como para nuestros equipos de operaciones, que reciben una notificación constante de que un cuadro no responde y debe reiniciarlo. No hay nada más significativo en esta casilla.

El problemaaparece para ser recogida de basura. Estamos utilizando el colector ConcurrentMarkSweep (como se señaló anteriormente) porque el colector STW original estaba causando tiempos de espera de JDBC y se hizo cada vez más lento. Los registros muestran que a medida que aumenta el uso de la memoria, esto comienza a generar fallas de cms y vuelve al colector original de detención del mundo, que luego parece no recopilarse correctamente.

Sin embargo, al ejecutar con jprofiler, el botón "Ejecutar GC" parece limpiar bien la memoria en lugar de mostrar una huella cada vez mayor, pero como no puedo conectar jprofiler directamente a la caja de producción, y la resolución de puntos de acceso probados no parece estar funcionando. Se fue con el vudú de tuning ciego de la recolección de basura.

Lo que he intentado:

Perfilado y fijación de hotspots.Uso de recolectores de basura STW, paralelos y CMS.Ejecución con tamaños de pila mín. / Máx. En incrementos de 1 / 2,2 / 4,4 / 5,6 / 6.Ejecución con espacio permgen en incrementos de 256M hasta 1Gb.Muchas combinaciones de las anteriores.También he consultado la JVM [referencia de optimización] (http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html), pero realmente no puedo encontrar nada que explique este comportamiento ni ningún ejemplo de _which_ tuning Parámetros a utilizar en una situación como esta.También he intentado (sin éxito) jprofiler en modo fuera de línea, conectándome con jconsole, visualvm, pero parece que no puedo encontrar nada que interperete mis datos de registro de gc.

Desafortunadamente, el problema también aparece de forma esporádica, parece ser impredecible, puede durar días o incluso una semana sin tener ningún problema, o puede fallar 40 veces en un día, y lo único que puedo detectar constantemente es Esa recolección de basura está mejorando.

¿Alguien puede dar algún consejo en cuanto a:
a) Por qué una JVM usa 8 conciertos físicos y 2 gb de espacio de intercambio cuando está configurada para un máximo de menos de 6.
b) Una referencia al ajuste de GC que en realidad explica o da ejemplos razonables de cuándo y con qué tipo de configuración utilizar las colecciones avanzadas.
c) Una referencia a las fugas de memoria java más comunes (entiendo las referencias no reclamadas, pero me refiero a nivel de biblioteca / marco, o algo más inherenet en las estructuras de datos, como los hashmaps).

Gracias por todos y cada uno de los conocimientos que puede proporcionar.

EDITAR
Emil H:
1) Sí, mi grupo de desarrollo es un espejo de los datos de producción, hasta el servidor de medios. La diferencia principal es el 32/64 bits y la cantidad de RAM disponible, que no puedo replicar muy fácilmente, pero el código, las consultas y la configuración son idénticos.

2) Hay un código heredado que se basa en JaxB, pero al reordenar los trabajos para intentar evitar conflictos de programación, generalmente la ejecución se elimina ya que se ejecuta una vez al día. El analizador principal utiliza consultas XPath que invocan el paquete java.xml.xpath. Esta fue la fuente de algunos hotspots, por ejemplo, las consultas no se estaban precompilando, y dos las referencias a ellas estaban en cadenas codificadas. Creé una memoria caché segura para hilos (hashmap) y factoré que las referencias a las consultas xpath fueran cadenas estáticas finales, lo que redujo significativamente el consumo de recursos. La consulta aún es una gran parte del procesamiento, pero debería ser porque esa es la principal responsabilidad de la aplicación.

3) Una nota adicional, el otro consumidor principal son las operaciones de imagen de JAI (reprocesamiento de imágenes desde un feed). No estoy familiarizado con las bibliotecas gráficas de java, pero por lo que he encontrado no son particularmente permeables.

(Gracias por las respuestas hasta ahora, amigos!)

ACTUALIZAR:
Pude conectarme a la instancia de producción con VisualVM, pero había deshabilitado la opción de visualización / ejecución GC (aunque podría verlo localmente). Lo interesante: la asignación del montón de la máquina virtual está obedeciendo a JAVA_OPTS, y el montón asignado real está sentado cómodamente en 1-1.5 gigas, y no parece tener fugas, pero el monitoreo de nivel de caja aún muestra un patrón de fugas, pero es No se refleja en el monitoreo de VM. No hay nada más corriendo en esta caja, así que estoy perplejo.

Respuestas a la pregunta(7)

Su respuesta a la pregunta