¿Qué consume memoria en el proceso de Java?


20

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


2
Tengo exactamente el mismo problema ... aquí está mi pregunta. stackoverflow.com/questions/18734389/… ¿Tiene una solución al respecto?
DeepNightTwo

Respuestas:


21

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.

00007f4394000000 324K rw --- [anon]
00007f4394051000 65212K ----- [anon]
00007f4398000000 560K rw --- [anon]
00007f439808c000 64976K ----- [anon]
00007f439c000000 620K rw --- [anon]
00007f439c09b000 64916K ----- [anon]

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.


55
configurar Xmx y Xms en el mismo valor, además de configurar la variable de entorno "export MALLOC_ARENA_MAX = 4" en el script sh que inicia nuestro servicio web ayudó en nuestro caso. Antes de eso, estábamos experimentando reinicios del servicio web debido a OOM Killer cada 2 a 8 horas. La versión GLIBC en Ubuntu 14.04 es 2.19, lo cual es bueno, ya que debe ser> = 2.16 para que la configuración MALLOC_ARENA_MAX funcione
Kluyg

Esta respuesta y el comentario anterior fueron un salvavidas para mí. En mi caso, MALLOC_ARENA_MAX = 1 fue necesario y efectivo.
John Bachir

3

¿Qué tal la sonda Lamdba ? Entre otras cosas, puede mostrar desgloses de uso de memoria similares a la captura de pantalla a continuación:

Vista de uso de memoria de la sonda Lambda

A veces pmap -x your_java_pidtambién puede ser útil.


Gracias por tu respuesta. Si entiendo correctamente, ¿entonces Lambda Probe es para Apache Tomcat? Lo que no usamos ... Con respecto al pmap, agregaré la información a la publicación superior
Konstantin S.

2

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.


2
Su fuga está fuera del montón, por lo que JProfiler realmente no ayudará aquí
Asaf Mesika

2

El problema está fuera del montón, por lo que los mejores candidatos son:

JNI leak  
Allocation of direct memory buffer

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.


1

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:

jmap -heap <PID>
jmap -permstat <PID>

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.


Gracias por su respuesta, pero el problema parece estar en algún lugar fuera del montón. Actualizaré la publicación superior para reflejar cierta información de jmap
Konstantin S.

¿Cuántos hilos tiene el proceso? El tamaño de la pila es de 2 MB por defecto en la mayoría de las plataformas, así que multiplíquelo por el número de hilos. Me sorprendería si explica toda la memoria "perdida" pero posiblemente parte de ella.
HampusLi

Alrededor de 300 hilos, tomados información de pmap tenemos un tamaño de pila de 1Mb. Y a partir de la misma salida de pmap, no parece que ninguna de esas pilas use más de 100Kb
Konstantin S.

0

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.