linux memory jvm yarn

¿Por qué una JVM informa más memoria comprometida que el tamaño de conjunto residente del proceso de Linux?



memory yarn (1)

Estoy empezando a sospechar que la memoria de la pila (a diferencia del montón JVM) parece estar comprometida sin convertirse en residente y, con el tiempo, se convierte en residente solo hasta el nivel más alto del uso real de la pila.

Sí, al menos en Linux mmap es vago a menos que se le indique lo contrario. Las páginas solo están respaldadas por la memoria física una vez que se escriben (las lecturas no son suficientes debido a la optimización de página cero )

La memoria de almacenamiento dinámico GC se toca de manera efectiva por el recopilador de copia o por la -XX:+AlwaysPreTouch cero -XX:+AlwaysPreTouch ( -XX:+AlwaysPreTouch ), por lo que siempre será residente. Las pilas de hilos otoh no se ven afectadas por esto.

Para obtener más confirmación, puede usar pmap -x <java pid> y hacer una referencia cruzada de RSS de varios rangos de direcciones con la salida del mapa de memoria virtual de NMT.

La memoria reservada se ha mapeado con PROT_NONE . Lo que significa que los rangos de espacio de direcciones virtuales tienen entradas en las estructuras vma del núcleo y, por lo tanto, no serán utilizadas por otras llamadas mmap / malloc. Pero seguirán provocando que las fallas de la página se envíen al proceso como SIGSEGV, es decir, acceder a ellas es un error.

Esto es importante para tener rangos de direcciones contiguas disponibles para uso futuro, lo que a su vez simplifica la aritmética del puntero.

La memoria comprometida pero no respaldada por almacenamiento se ha asignado con, por ejemplo, PROT_READ | PROT_WRITE PROT_READ | PROT_WRITE pero acceder a él todavía causa un error de página. Pero esa falla de la página es manejada silenciosamente por el núcleo al respaldarla con memoria real y regresar a la ejecución como si nada hubiera pasado.
Es decir, es un detalle de implementación / optimización que el proceso en sí no notará.

Para dar un desglose de los conceptos:

Montón utilizado : la cantidad de memoria ocupada por objetos vivos según el último GC

Comprometido : rangos de direcciones que se han asignado con algo diferente a PROT_NONE. Pueden estar respaldados o no por cambios físicos o de intercambio debido a la asignación diferida y la paginación.

Reservado : el rango total de direcciones que se ha mapeado previamente a través de mmap para un grupo de memoria en particular.
La diferencia reservada y confirmada consiste en asignaciones PROT_NONE , que se garantiza que no estarán respaldadas por la memoria física

Residente : páginas que se encuentran actualmente en RAM física. Esto significa código, pilas, parte de los grupos de memoria comprometidos, pero también partes de archivos mmaped a los que se ha accedido recientemente y asignaciones fuera del control de la JVM.

Virtual : la suma de todas las asignaciones de direcciones virtuales. Cubre agrupaciones de memoria reservadas y comprometidas, pero también archivos asignados o memoria compartida. Este número rara vez es informativo ya que la JVM puede reservar rangos de direcciones muy grandes por adelantado o archivos grandes de mmap.

Al ejecutar una aplicación Java (en YARN) con el seguimiento de memoria nativa habilitado ( -XX:NativeMemoryTracking=detail consulte https://docs.oracle.com/javase/8/docs/technotes/guides/vm/nmt-8.html y https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html ), puedo ver cuánta memoria está utilizando la JVM en diferentes categorías.

Mi aplicación en jdk 1.8.0_45 muestra:

Native Memory Tracking: Total: reserved=4023326KB, committed=2762382KB - Java Heap (reserved=1331200KB, committed=1331200KB) (mmap: reserved=1331200KB, committed=1331200KB) - Class (reserved=1108143KB, committed=64559KB) (classes #8621) (malloc=6319KB #17371) (mmap: reserved=1101824KB, committed=58240KB) - Thread (reserved=1190668KB, committed=1190668KB) (thread #1154) (stack: reserved=1185284KB, committed=1185284KB) (malloc=3809KB #5771) (arena=1575KB #2306) - Code (reserved=255744KB, committed=38384KB) (malloc=6144KB #8858) (mmap: reserved=249600KB, committed=32240KB) - GC (reserved=54995KB, committed=54995KB) (malloc=5775KB #217) (mmap: reserved=49220KB, committed=49220KB) - Compiler (reserved=267KB, committed=267KB) (malloc=137KB #333) (arena=131KB #3) - Internal (reserved=65106KB, committed=65106KB) (malloc=65074KB #29652) (mmap: reserved=32KB, committed=32KB) - Symbol (reserved=13622KB, committed=13622KB) (malloc=12016KB #128199) (arena=1606KB #1) - Native Memory Tracking (reserved=3361KB, committed=3361KB) (malloc=287KB #3994) (tracking overhead=3075KB) - Arena Chunk (reserved=220KB, committed=220KB) (malloc=220KB)

Esto muestra 2,7 GB de memoria comprometida, incluidos 1,3 GB de almacenamiento dinámico asignado y casi 1,2 GB de pilas de subprocesos asignados (utilizando muchos subprocesos).

Sin embargo, cuando se ejecuta ps ax -o pid,rss | grep <mypid> ps ax -o pid,rss | grep <mypid> o top muestra solo 1,6 GB de memoria residente RES/rss . El intercambio de cheques dice que ninguno está en uso:

free -m total used free shared buffers cached Mem: 129180 99348 29831 0 2689 73024 -/+ buffers/cache: 23633 105546 Swap: 15624 0 15624

¿Por qué la JVM indica que la memoria de 2.7GB está comprometida cuando solo residen 1.6GB? ¿A dónde fue el resto?