Pourquoi une JVM déclare - t-elle plus de mémoire engagée que la taille de l'ensemble des résidents du processus linux?

lors de l'exécution D'une application Java (en fil) avec suivi de mémoire natif activé ( -XX:NativeMemoryTracking=detail voir https://docs.oracle.com/javase/8/docs/technotes/guides/vm/nmt-8.html et https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html ), je peux voir combien de mémoire la JVM utilise dans différentes catégories.

mon application sur JDK 1.8.0_45 montre:

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) 

cela montre 2,7 Go de mémoire engagée, dont 1,3 Go de tas alloué et près de 1,2 Go de piles de threads alloués (utilisant de nombreux threads).

cependant, quand on exécute ps ax -o pid,rss | grep <mypid> ou top il montre seulement 1,6 Go de RES/rss mémoire résidente. La vérification de swap dit rien en usage:

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

pourquoi la JVM indique-t-elle que la mémoire de 2,7 GO est engagée alors que seulement 1,6 Go est résident? D'où vient le reste?

20
demandé sur Dave L. 2015-07-02 02:36:42

1 réponses

je commence à soupçonner que la mémoire de pile (à la différence du tas JVM) semble être préengagée sans devenir résident et au fil du temps devient résident seulement jusqu'à la marque d'eau élevée de l'utilisation réelle de pile.

Oui, malloc/mmap est paresseux sauf indication contraire. Les Pages ne sont sauvegardées par mémoire physique qu'une fois accessibles.

GC mémoire tas est effectivement touché par le collecteur de copie ou par pré-zéroing ( -XX:+AlwaysPreTouch ), donc il sera toujours résident. Les piles de fils otoh ne sont pas affectées par cela.

pour plus de confirmation vous pouvez utiliser pmap -x <java pid> et croiser le RSS de diverses gammes d'adresses avec la sortie de la carte mémoire virtuelle de NMT.


mémoire Réservée a été mmaped avec PROT_NONE . Ce qui signifie que les plages d'adresses virtuelles ont des entrées dans les structures vma du noyau et ne seront donc pas utilisées par autres appels mmap / malloc. Mais ils provoqueront toujours des défauts de page transmis au processus en tant que SIGSEGV, c'est-à-dire que leur accès est une erreur.

C'est important d'avoir contiguë plages d'adresses disponibles pour une utilisation future, ce qui simplifie l'arithmétique des pointeurs.

Committed-but-not-backed-by-storage memory a été mappé avec - par exemple - PROT_READ | PROT_WRITE mais y accéder provoque toujours une erreur de page. Mais cette faute de page est silencieusement traitée par le noyau en le sauvegardant avec la mémoire réelle et en retournant à l'exécution comme si rien ne s'était passé.

I. E. c'est un détail d'implémentation/optimisation qui ne sera pas remarqué par le processus lui-même.


pour donner une ventilation des concepts:

tas utilisé : la quantité de mémoire occupée par les objets vivants selon le dernier GC

commis : plages D'adresses qui ont été cartographiées avec quelque chose d'autre que PROT_NONE. Ils peuvent être ou ne pas être soutenus par physique ou de swap en raison de l'allocation paresseuse et la pagination.

réservé : la plage d'adresses totale qui a été pré-mappée via mmap pour un pool de mémoire particulier.

La différence réservé-engagé se compose de PROT_NONE mappings, qui sont garantis de ne pas être soutenu par la mémoire physique

Resident : Pages qui sont actuellement en mémoire vive. Cela signifie le code, Les piles, une partie des fonds de mémoire engagés, mais aussi des parties des fichiers mm enregistrés qui ont été récemment consultés et des attributions hors du contrôle de la JVM.

virtuel : la somme de toutes les correspondances d'adresses virtuelles. Couvre les pools de mémoire réservés, mais aussi les fichiers mappés ou la mémoire partagée. Ce nombre est rarement informatif puisque la JVM peut réserver à l'avance des plages d'adresses très larges ou des fichiers mmap de grande taille.

21
répondu the8472 2018-01-04 06:10:13