为什么JVM报告的内存比linux进程驻留集大小更多?

Dav*_* L. 28 linux memory jvm hadoop-yarn

在运行启用了本机内存跟踪的Java应用程序(在YARN中)时(-XX:NativeMemoryTracking=detail请参阅https://docs.oracle.com/javase/8/docs/technotes/guides/vm/nmt-8.htmlhttps:// docs. oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html),我可以看到JVM在不同类别中使用了多少内存.

我在jdk 1.8.0_45上的应用程序显示:

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) 

这显示了2.7GB的已提交内存,包括1.3GB的已分配堆和近1.2GB的已分配线程堆栈(使用多个线程).

但是,在运行时ps ax -o pid,rss | grep <mypid>top它只显示1.6GB的RES/rss驻留内存.检查交换说没有使用:

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

当只有1.6GB的驻留时,为什么JVM表示提交了2.7GB的内存?其余的去哪儿了?

the*_*472 31

我开始怀疑堆栈内存(与JVM堆不同)似乎是预先提交的,而不会成为常驻,并且随着时间的推移变成仅驻留在实际堆栈使用的高水位线上.

是的,除非另有说明,否则malloc/mmap是懒惰的.一旦访问页面,页面仅由物理内存支持.

GC堆内存有效地被复制收集器或pre-zeroing(-XX:+AlwaysPreTouch)触及,因此它总是驻留的.线程堆栈otoh不受此影响.

为了进一步确认,您可以使用pmap -x <java pid>和交叉引用各种地址范围的RSS和来自NMT的虚拟内存映射的输出.


已保留的内存已被编码PROT_NONE.这意味着虚拟地址空间范围在内核的vma结构中具有条目,因此不会被其他mmap/malloc调用使用.但它们仍会导致页面错误以SIGSEGV的形式转发到进程,即访问它们是一个错误.

这对于将来可以使用的连续地址范围很重要,这反过来又简化了指针算法.

已经映射了已提交但未备份的存储内存 - 例如 - PROT_READ | PROT_WRITE但访问它仍会导致页面错误.但是,内核通过用实际内存支持并返回执行就好像没有发生任何事情来静默处理该页面错误.
即它是一个实现细节/优化,不会被进程本身注意到.


给出概念的细分:

Used Heap:根据上一个GC,活动对象占用的内存量

已提交:已使用PROT_NONE以外的其他内容映射的地址范围.由于延迟分配和分页,它们可能会或可能不会由物理或交换支持.

保留:已mmap为特定内存池预先映射的总地址范围.
预留-致力于差由PROT_NONE映射,这是保证不被物理存储器被备份

居民:目前处于物理公羊的页面.这意味着代码,堆栈,已提交内存池的一部分,但也包括最近访问过的mmaped文件的一部分以及JVM控制之外的分配.

虚拟:所有虚拟地址映射的总和.涵盖已提交,保留的内存池以及映射文件或共享内存.这个数字很少提供信息,因为JVM可以提前预留非常大的地址范围或mmap大文件.

  • 感谢您如此快速的回复。那么 sys 调用 mprotect 怎么样:http://man7.org/linux/man-pages/man2/mprotect.2.html ?看来mproctect也可以改变vma的prot。 (2认同)