Estamos tratando de investigar el uso de memoria del proceso de Java bajo carga moderada.
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
12663 test 20 0 8378m 6.0g 4492 S 43 8.4 162:29.95 java
Como puede ver, tenemos memoria residente a 6Gb. Ahora la parte interesante es esta: el proceso se ejecuta con estos parámetros:
- -Xmx2048m
- -Xms2048m
- -XX: NewSize = 512m
- -XX: MaxDirectMemorySize = 256m
- ... algunos otros para GC y esas cosas
Al observar estas configuraciones y el uso real de la memoria, nos topamos con la diferencia de lo que esperamos que este proceso use y lo que realmente usa.
Por lo general, nuestros problemas de memoria se resuelven analizando el volcado de almacenamiento dinámico, pero en este caso nuestra memoria se usa en algún lugar fuera del almacenamiento dinámico.
Preguntas: ¿Cuáles serían los pasos para tratar de encontrar la razón de un uso de memoria tan alto? ¿Qué herramientas podrían ayudarnos a identificar qué utiliza la memoria en ese proceso?
EDITAR 0
No parece que este sea un problema relacionado con el montón, ya que todavía tenemos bastante espacio allí:
jmap -heap 12663
resultados en (editado para ahorrar espacio)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 536870912 (512.0MB)
MaxNewSize = 536870912 (512.0MB)
OldSize = 1610612736 (1536.0MB)
NewRatio = 7
SurvivorRatio = 8
PermSize = 21757952 (20.75MB)
MaxPermSize = 85983232 (82.0MB)
New Generation: 45.7% used
Eden Space: 46.3% used
From Space: 41.4% used
To Space: 0.0% used
concurrent mark-sweep generation: 63.7% used
Perm Generation: 82.5% used
EDITAR 1
usando el pmap podemos ver que hay bastante cantidad de asignaciones de 64Mb:
pmap -x 12663 | grep rwx | sort -n -k3 | less
resultados en:
... a lot more of these 64Mb chunks
00007f32b8000000 0 65508 65508 rwx-- [ anon ] <- what are these?
00007f32ac000000 0 65512 65512 rwx-- [ anon ]
00007f3268000000 0 65516 65516 rwx-- [ anon ]
00007f3324000000 0 65516 65516 rwx-- [ anon ]
00007f32c0000000 0 65520 65520 rwx-- [ anon ]
00007f3314000000 0 65528 65528 rwx-- [ anon ]
00000000401cf000 0 241904 240980 rwx-- [ anon ] <- Direct memory ?
000000077ae00000 0 2139688 2139048 rwx-- [ anon ] <- Heap ?
Entonces, ¿cómo averiguar cuáles son esos trozos de 64Mb? ¿Qué los está usando? ¿Qué tipo de datos hay en ellos?
Gracias
Respuestas:
El problema podría estar relacionado con este problema de glibc .
Básicamente, cuando tiene múltiples subprocesos que asignan memoria, glibc aumentará el número de arenas disponibles para realizar la asignación para evitar la contención de bloqueo. Una arena es 64Mb grande. El límite superior es crear 8 veces el número de arenas de núcleos. Las arenas se crearán a pedido cuando un hilo acceda a una arena que ya está bloqueada para que crezca con el tiempo.
En Java, donde se espolvorea con hilos, esto puede conducir rápidamente a la creación de muchos escenarios. Y hay asignaciones repartidas por todas estas arenas. Inicialmente, cada arena de 64Mb es solo un mapa de memoria no comprometida, pero a medida que realiza asignaciones, comienza a usar memoria real para ellas.
Su pmap probablemente tenga listados similares a este a continuación. Observe cómo 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. Es decir, suman hasta 64Mb.
En cuanto a las soluciones : el error menciona algunos parámetros del entorno que puede establecer para limitar el número de arenas, pero necesita una versión glibc lo suficientemente alta.
fuente
¿Qué tal la sonda Lamdba ? Entre otras cosas, puede mostrar desgloses de uso de memoria similares a la captura de pantalla a continuación:
A veces
pmap -x your_java_pid
también puede ser útil.fuente
JProfiler podría ser algo que buscas pero no es gratis. Otra herramienta buena y gratuita para investigar el uso de memoria del proceso de Java es Java VisualVM disponible como una herramienta JDK en distribuciones Oracle / Sun JDK. Yo personalmente recomendaría un enfoque más holístico del problema (es decir, para monitorear discos JDK + OS +, etc.): el uso de algún sistema de monitoreo de red: Nagios, Verax NMS u OpenNMS.
fuente
El problema está fuera del montón, por lo que los mejores candidatos son:
Debido al hecho de que ha limitado el tamaño del búfer directo, el mejor candidato en mi opinión es la fuga JNI.
fuente
Hay una herramienta útil para ver la asignación de la memoria de almacenamiento dinámico incluida en el JDK llamada jmap, además de esto también tienes stack etc (Xss). Ejecute estos dos comandos jmap para obtener más información sobre el uso de la memoria:
Para obtener más información, puede conectarse al proceso con jconsole (también incluido en el JDK). Sin embargo, Jconsole requiere que JMX se configure en la aplicación.
fuente
Use JVisualVM. Tiene varias vistas diferentes que le dirán cuánta memoria de almacenamiento dinámico está en uso, PermGen, etc.
En cuanto a responder tu pregunta. Java maneja la memoria de manera muy diferente de lo que podría esperar.
Cuando configura los parámetros -Xms y -Xmx, le está diciendo a la JVM cuánta memoria debería asignar en el montón para comenzar y también cuánto debería asignar como máximo.
Si tiene una aplicación java que utilizó un total de 1 m de memoria pero pasó en -Xms256m -Xmx2g, la JVM se inicializará con 256 m de memoria utilizada. No usará menos que eso. No importa que su aplicación use solo 1 m de memoria.
En segundo lugar. En el caso anterior, si su aplicación en algún momento usa más de 256 m de memoria, la JVM asignará tanta memoria como sea necesaria para atender la solicitud. Sin embargo, no reducirá el tamaño del montón al valor mínimo. Al menos, no en la mayoría de las circunstancias.
En su caso, dado que está configurando la memoria mínima y máxima en 2g, la JVM asignará 2g al inicio y la mantendrá.
La gestión de la memoria Java es bastante compleja y ajustar el uso de la memoria puede ser una tarea en sí misma. Sin embargo, existen muchos recursos que pueden ayudar.
fuente