xmx - El uso de la memoria del proceso Java sigue aumentando infinitamente
tomcat set memory heap size (3)
¿Puede volver a ejecutar sus pruebas con esta opción -XX:MaxDirectMemorySize=1024m
? El valor exacto de este límite no importa, pero muestra posibles "fugas" .
¿También puede proporcionar detalles de GC ( -XX:+PrintGC
)?
java.nio.ByteBuffer es una posible causa de ellos debido a su finalización específica.
ACTUALIZACIÓN # 1
He visto un comportamiento similar por otras dos razones: java.misc.Unsafe (poco probable) y llamadas JNI de alta carga.
Es difícil de entender sin perfil de la prueba.
ACTUALIZACIÓN # 2
Tanto el método de llamadas JNI de carga alta como el método finalize () causan el problema descrito, ya que los objetos no tienen tiempo suficiente para finalizar.
El fragmento juzip.Inflater
continuación:
/**
* Closes the decompressor when garbage is collected.
*/
protected void finalize() {
end();
}
/**
* Closes the decompressor and discards any unprocessed input.
* This method should be called when the decompressor is no longer
* being used, but will also be called automatically by the finalize()
* method. Once this method is called, the behavior of the Inflater
* object is undefined.
*/
public void end() {
synchronized (zsRef) {
long addr = zsRef.address();
zsRef.clear();
if (addr != 0) {
end(addr);
buf = null;
}
}
}
private native static void end(long addr);
Condiciones previas:
- PC con 16 Gb de RAM
- JDK 1.8.x instalado en Ubuntu 16.10 x64.
- una aplicación web estándar basada en Spring, que se implementa en Tomcat 8.5.x. Tomcat se configura con los siguientes parámetros:
CATALINA_OPTS="$CATALINA_OPTS -Xms128m -Xmx512m -XX:NewSize=64m -XX:MaxNewSize=128m -Xss512k -XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods -XX:MaxMetaspaceSize=512m -XX:-TieredCompilation -XX:ReservedCodeCacheSize=512m"
- JMeter 2.13 para pruebas de carga en ejecución
- JProfiler 9.x para el seguimiento del uso de memoria del montón java
-
top
util para el seguimiento de uso de memoria de proceso java
Cuando comienzo las pruebas de carga secuencialmente 3 veces observo (usando top
) que el proceso java está aumentando la cantidad de memoria utilizada:
- después de que Tomcat comience usa ~ 1Gb
- después de la primera prueba de ejecución usa 4.5Gb
- Cuando todas las pruebas terminan, Tomcat está usando 7 Gb de RAM
Todo este tiempo el tamaño del montón es limitado y JProfiler confirma que: el tamaño del montón no excede los 512Mb.
Esta es una captura de pantalla de JProfiler. Los números rojos en la parte inferior son el tamaño de memoria utilizado por el proceso Java (de acuerdo con la top
).
La pregunta es: ¿por qué el proceso java sigue aumentando el uso de la memoria todo el tiempo mientras funciona?
¡Gracias!
UPD # 1: Sobre el posible duplicado: have confirmed that this only happens on Solaris.
pero yo uso Ubuntu 16.10. Además, la pregunta señalada no tiene una respuesta que explique la causa del problema.
UPD # 2: Tuve que volver a este problema después de una pausa. Y ahora uso pmap
util para hacer un volcado de memoria utilizado por el proceso java
. Tengo tres volcados: antes de ejecutar las pruebas, después de la primera ejecución de las pruebas y después de algunas ejecuciones de las N pruebas. Las pruebas que producen mucho tráfico a la aplicación. Todos los volcados están aquí: https://gist.github.com/proshin-roman/752cea2dc25cde64b30514ed9ed9bbd0 . Son bastante grandes, pero las cosas más interesantes están en la octava línea con el tamaño del montón: se necesitan 282.272 Kb
antes de las pruebas y 3.036.400 Kb
finalmente, ¡más de 10 veces de diferencia! Y está creciendo cada vez que hago pruebas. Al mismo tiempo, el tamaño del almacenamiento dinámico es constante (según JProfiler / VisualVM). ¿Qué opciones tengo para encontrar la causa de este problema? ¿Depurar JVM? He intentado encontrar alguna forma de "mirar" este segmento de la memoria pero he fallado. Asi que:
- ¿Puedo identificar de alguna manera el contenido del segmento
[heap]
de memoria? - ¿Se espera tal comportamiento de java?
Apreciaré cualquier consejo sobre este problema. ¡Gracias a todos!
UPD # 3 : usando jemalloc (gracias a @ivan por la idea) obtuve la siguiente imagen:
Y parece que tengo casi el mismo problema que se describe aquí: http://www.evanjones.ca/java-native-leak-bug.html
UPD # 4 : por ahora descubrí que el problema está relacionado con java.util.zip.Inflater / Deflater y estas clases se usan en muchos lugares en mi aplicación. Pero el mayor impacto en el consumo de memoria hace que la interacción con el servicio SOAP se elimine. Mi aplicación utiliza la implementación de referencia del estándar JAX-WS y proporcionó el siguiente consumo de memoria bajo carga (tiene una precisión baja después de 10 Gb): Luego hice las mismas pruebas de carga pero con la implementación de Apache CXF y obtuve el siguiente resultado: Así que puedes ver que CXF usa menos memoria y es más estable (no está creciendo todo el tiempo como ref.impl). Finalmente, encontré un problema en el rastreador de problemas de JDK, https://bugs.openjdk.java.net/browse/JDK-8074108 : se trata nuevamente de pérdidas de memoria en la biblioteca zip y el problema aún no está cerrado. Así que parece que realmente no puedo solucionar el problema con las fugas de memoria en mi aplicación, solo puedo hacer una solución.
¡Gracias a todos por su ayuda!
Basado en la máquina de afeitar de Occam: ¿no podría ser que haya una pérdida de memoria en algún lugar (es decir, "retenciones de objetos no intencionales" a''la Effective Java Item 6)?
Mi hipótesis es que usted recopila información de asignación / pilas de llamadas / etc en JProfiler y el crecimiento de RSS que observa está relacionado con JProfiler que guarda esos datos en la memoria.
Puede verificar si eso es cierto al recopilar menos información (debería haber una pantalla al inicio del perfil que le permita, por ejemplo, no recopilar asignaciones de objetos) y ver si observa un menor crecimiento de RSS como resultado. Ejecutar su prueba de carga sin JProfiler también es una opción.
Tuve un caso similar en el pasado.