permitido - Diferencia entre el tamaño del conjunto residente(RSS) y la memoria total comprometida de Java(NMT) para una JVM que se ejecuta en el contenedor Docker
content is not allowed in prolog (1)
Tienes alguna pista sobre " Análisis del uso de la memoria Java en un contenedor Docker " de Mikhail Krestjaninoff :
R esident S et Size es la cantidad de memoria física actualmente asignada y utilizada por un proceso (sin páginas intercambiadas). Incluye el código, los datos y las bibliotecas compartidas (que se cuentan en cada proceso que los utiliza)
¿Por qué la información de las estadísticas de Docker difiere de los datos de ps?
La respuesta a la primera pregunta es muy simple: Docker tiene un error (o una función, depende de tu estado de ánimo) : incluye cachés de archivos en la información de uso total de la memoria. Entonces, podemos evitar esta métrica y usar información de
ps
sobre RSS.Bueno, está bien, pero ¿por qué RSS es más alto que Xmx?
Teóricamente, en el caso de una aplicación java
RSS = Heap size + MetaSpace + OffHeap size
donde OffHeap consta de subprocesos de subprocesos, búferes directos, archivos asignados (bibliotecas y jarras) y código JVM itse
¡Desde JDK 1.8.40 tenemos Native Memory Tracker !
Como puede ver, ya he agregado
-XX:NativeMemoryTracking=summary
propiedad de-XX:NativeMemoryTracking=summary
a la JVM, por lo que podemos invocarlo desde la línea de comando:
docker exec my-app jcmd 1 VM.native_memory summary
(Esto es lo que hizo el OP)
No se preocupe por la sección "Desconocido": parece que NMT es una herramienta inmadura y no puede tratar con CMS GC (esta sección desaparece cuando usa otro GC).
Tenga en cuenta que NMT muestra la memoria "comprometida", no "residente" (que obtiene a través del comando ps). En otras palabras, una página de memoria puede comprometerse sin considerar como residente (hasta que se acceda directamente) .
Eso significa que los resultados de NMT para áreas que no son de pila (el montón siempre está preinicializado) podrían ser mayores que los valores de RSS .
(Aquí es donde aparece " ¿Por qué una JVM informa más memoria comprometida que el tamaño de residente del proceso de Linux? ")
Como resultado, a pesar del hecho de que establecemos el límite del montón de jvm en 256 m, nuestra aplicación consume 367M. Los "otros" 164M se utilizan principalmente para almacenar metadatos de clase, código compilado, subprocesos y datos de GC.
Los primeros tres puntos a menudo son constantes para una aplicación, por lo que lo único que aumenta con el tamaño del montón son los datos de GC.
Esta dependencia es lineal, pero el coeficiente "k
" (y = kx + b
) es mucho menor que 1.
En términos más generales, esto parece ser seguido por el número 15020 que informa un problema similar desde el acoplador 1.7
Estoy ejecutando una aplicación Scala (JVM) simple que carga una gran cantidad de datos dentro y fuera de la memoria.
Configuro la JVM en un montón de 8G (-Xmx8G
). Tengo una máquina con 132G de memoria, y no puede manejar más de 7-8 contenedores porque crecen mucho más allá del límite de 8G que impuse en la JVM.
(La docker stat
fue reportada como engañosa antes , ya que aparentemente incluye cachés de archivos en la información de uso de memoria total)
docker stat
muestra que cada contenedor usa mucha más memoria de la que se supone que debe usar JVM. Por ejemplo:
CONTAINER CPU % MEM USAGE/LIMIT MEM % NET I/O
dave-1 3.55% 10.61 GB/135.3 GB 7.85% 7.132 MB/959.9 MB
perf-1 3.63% 16.51 GB/135.3 GB 12.21% 30.71 MB/5.115 GB
Casi parece que la JVM le pide al sistema operativo la memoria, que se asigna dentro del contenedor, y la JVM libera la memoria cuando se ejecuta su GC, pero el contenedor no libera la memoria al sistema operativo principal. Entonces ... fuga de memoria.
Guión:
Tengo una JVM ejecutándose en un contenedor acoplable. Hice algunos análisis de memoria usando dos herramientas: 1) arriba 2) Seguimiento de memoria nativa de Java . Los números parecen confusos y estoy tratando de encontrar cuál está causando las diferencias.
Pregunta:
El RSS se reporta como 1272MB para el proceso de Java y la memoria total de Java se reporta como 790.55 MB. ¿Cómo puedo explicar de dónde salió el resto de la memoria 1272 - 790.55 = 481.44 MB?
Por qué quiero mantener este problema abierto incluso después de ver esta pregunta en SO:
Vi la respuesta y la explicación tiene sentido. Sin embargo, después de obtener resultados de Java NMT y pmap -x, todavía no soy capaz de asignar de forma concreta qué direcciones de memoria Java son residentes y físicamente mapeadas . Necesito una explicación concreta (con pasos detallados) para encontrar cuál es la causa de esta diferencia entre la memoria comprometida de RSS y Java Total.
Salida superior
Java NMT
Estadísticas de memoria Docker
Gráficos
Tengo un contenedor acoplable funcionando durante más de 48 horas. Ahora, cuando veo un gráfico que contiene:
- Memoria total otorgada al contenedor de acoplador = 2 GB
- Java Max Heap = 1 GB
- Total comprometido (JVM) = siempre menos de 800 MB
- Heap Used (JVM) = siempre menos de 200 MB
- Non Heap Used (JVM) = siempre menos de 100 MB.
- RSS = alrededor de 1.1 GB.
Entonces, ¿qué está comiendo la memoria entre 1.1 GB (RSS) y 800 MB (memoria total comprometida de Java)?