Este es un problema que he estado tratando de localizar durante un par de meses. Tengo una aplicación Java en ejecución 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 localizar.
Antecedentes: en la caja de producción (donde el problema es más notorio), no tengo un acceso particularmente bueno a la caja y no he podido ejecutar Jprofiler. Esa caja es una máquina de 64 bits de cuatro núcleos y 8 GB 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.2
- Java 6u11
- Tomcat 6
- Primavera / WebMVC 2.5
- Hibernar 3
- Cuarzo 1.6.1
- DBCP 1.2.1
- Mysql 5.0.45
- Ehcache 1.5.0
- (y, por supuesto, una serie de otras dependencias, en particular las bibliotecas jakarta-commons)
Lo más cerca que puedo estar de reproducir el problema es una máquina de 32 bits con menores requisitos de memoria. Sobre eso tengo control. 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 de la agrupación de subprocesos y eliminación de la búsqueda previa de hibernación innecesaria y "calentamiento de caché" excesivo durante el procesamiento).
En cada caso, el generador de perfiles mostró que estos consumían grandes cantidades de recursos por una razón u otra, y que ya no eran los acaparadores de recursos primarios una vez que se introdujeron los cambios.
El problema: La JVM parece ignorar por completo la configuración de uso de la memoria, llena toda la memoria y deja de responder. Este es un problema para el cliente final, que espera una encuesta periódica (5 minutos y reintento de 1 minuto), así como para nuestros equipos de operaciones, a quienes se les notifica constantemente que una caja no responde y tienen que reiniciarla. No hay nada más significativo en ejecución en esta caja.
El problema parece ser la recolección de basura. Estamos utilizando el recopilador ConcurrentMarkSweep (como se indicó anteriormente) porque el recopilador STW original estaba provocando tiempos de espera de JDBC y se volvió cada vez más lento. Los registros muestran que a medida que aumenta el uso de la memoria, eso comienza a generar fallas de cms y regresa al colector original de stop-the-world, que luego parece no recopilar correctamente.
Sin embargo, al ejecutar jprofiler, el botón "Ejecutar GC" parece limpiar la memoria muy bien en lugar de mostrar una huella creciente, 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, estoy Se fue con el vudú de ajustar a ciegas Garbage Collection.
Lo que he probado:
- Perfilado y fijación de puntos calientes.
- Utilizando recolectores de basura STW, Parallel y CMS.
- Ejecutando con tamaños de montón mínimo / máximo en incrementos de 1 / 2,2 / 4,4 / 5,6 / 6.
- Se ejecuta con espacio permgen en incrementos de 256 M hasta 1 Gb.
- Muchas combinaciones de las anteriores.
- También he consultado la JVM [referencia de ajuste] (http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html), pero no puedo encontrar nada que explique este comportamiento ni ningún ejemplo de _which_ tuning parámetros para usar en una situación como esta.
- También probé (sin éxito) jprofiler en modo fuera de línea, conectándome con jconsole, visualvm, pero parece que no puedo encontrar nada que interpere mis datos de registro de gc.
Desafortunadamente, el problema también aparece esporádicamente, parece ser impredecible, puede funcionar durante días o incluso una semana sin tener ningún problema, o puede fallar 40 veces al día, y lo único que puedo detectar consistentemente es esa recolección de basura está actuando mal.
¿Alguien puede dar algún consejo sobre:
a) Por qué una JVM está usando 8 gigas físicas 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 realmente explica o da ejemplos razonables de cuándo y con qué tipo de configuración usar las colecciones avanzadas.
c) Una referencia a las fugas de memoria de Java más comunes (entiendo referencias no reclamadas, pero me refiero a nivel de biblioteca / marco, o algo más inherente en estructuras de datos, como 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 principal diferencia 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 algún código heredado que se basa en JaxB, pero al reordenar los trabajos para tratar de evitar conflictos de programación, generalmente elimino esa ejecución, ya que se ejecuta una vez al día. El analizador principal utiliza consultas XPath que llaman al paquete java.xml.xpath. Esta fue la fuente de algunos puntos de acceso, por un lado, las consultas no se estaban compilando previamente, y dos, las referencias a ellas estaban en cadenas codificadas. Creé un caché seguro para subprocesos (hashmap) y factoricé las referencias a las consultas de xpath para que fueran cadenas estáticas finales, lo que redujo significativamente el consumo de recursos. La consulta sigue siendo una gran parte del procesamiento, pero debería serlo porque esa es la principal responsabilidad de la aplicación.
3) Una nota adicional, el otro consumidor principal son las operaciones de imágenes de JAI (reprocesamiento de imágenes de una fuente). No estoy familiarizado con las bibliotecas gráficas de Java, pero por lo que he encontrado, no tienen fugas particularmente.
(¡Gracias por las respuestas hasta ahora, amigos!)
ACTUALIZACIÓN:
pude conectarme a la instancia de producción con VisualVM, pero había deshabilitado la opción GC visualization / run-GC (aunque pude verlo localmente). Lo interesante: la asignación de pila de la VM obedece a JAVA_OPTS, y la pila asignada real se sienta cómodamente en 1-1.5 gigas, y no parece tener fugas, pero el monitoreo de nivel de caja todavía muestra un patrón de fugas, pero es no se refleja en el monitoreo de VM. No hay nada más ejecutándose en esta caja, así que estoy perplejo.