在启用本机内存跟踪的情况下运行Java应用程序(在YARN中)时( -XX:NativeMemoryTracking=detail
请参阅https://docs.oracle.com/javase/8/docs/technotes/guides/vm/nmt-8.html和https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html ),我可以看到JVM在不同类别中使用了多less内存。
我在jdk 1.8.0_45上的应用程序显示:
本机内存跟踪: 总计:保留= 4023326KB,承诺= 2762382KB - Java堆(保留= 1331200KB,承诺= 1331200KB) (mmap:reserved = 1331200KB,承诺= 1331200KB) - 类(保留= 1108143KB,承诺= 64559KB) (课#8621) (malloc = 6319KB#17371) (mmap:reserved = 1101824KB,承诺= 58240KB) - 线程(保留= 1190668KB,承诺= 1190668KB) (线程#1154) (stack:reserved = 1185284KB,承诺= 1185284KB) (malloc = 3809KB#5771) (舞台= 1575KB#2306) - 代码(保留= 255744KB,承诺= 38384KB) (malloc = 6144KB#8858) (mmap:reserved = 249600KB,承诺= 32240KB) - GC(保留= 54995KB,承诺= 54995KB) (malloc = 5775KB#217) (mmap:reserved = 49220KB,承诺= 49220KB) - 编译器(保留= 267KB,承诺= 267KB) (malloc = 137KB#333) (竞技场= 131KB#3) - 内部(保留= 65106KB,承诺= 65106KB) (malloc = 65074KB#29652) (mmap:保留= 32KB,提交= 32KB) - 符号(保留= 13622KB,承诺= 13622KB) (malloc = 12016KB#128199) (舞台= 1606KB#1) - 本机内存跟踪(保留= 3361KB,承诺= 3361KB) (malloc = 287KB#3994) (跟踪开销= 3075KB) - 竞技场块(保留= 220KB,承诺= 220KB) (malloc的= 220KB)
这显示了2.7GB的提交内存,包括1.3GB的分配堆和几乎1.2GB的分配线程堆栈(使用许multithreading)。
但是,运行ps ax -o pid,rss | grep <mypid>
ps ax -o pid,rss | grep <mypid>
或top
只显示1.6GB的RES/rss
驻留内存。 检查交换说没有在使用中:
免费-m caching总共使用的空闲共享缓冲区 Mem:129180 99348 29831 0 2689 73024 - / + buffers / cache:23633 105546 交换:15624 0 15624
为什么JVM指出只有1.6GB驻留时提交2.7GB内存? 剩下的去哪了?
我开始怀疑堆栈内存(与JVM堆不同)似乎是预先提供的,而不会成为常驻,随着时间的推移,堆栈内存只会成为实际堆栈使用的高水位。
是的,除非另有说明,malloc / mmap是懒惰的。 页面访问后只能由物理内存支持。
GC堆内存有效地被复制收集器或预置( -XX:+AlwaysPreTouch
)所-XX:+AlwaysPreTouch
,所以它始终是常驻的。 线程堆栈otoh不受此影响。
为了进一步确认,你可以使用pmap -x <java pid>
并用NMT的虚拟内存映射的输出来交叉引用各种地址范围的RSS。
保留的内存已经用PROT_NONE
。 这意味着虚拟地址空间范围在页表中有条目,因此不会被其他mmap / malloc调用使用。 但是,它们仍然会导致页面错误作为SIGSEGV转发到进程,即访问它们是一个错误。
连续的地址范围可供将来使用,这对于简化指针运算来说非常重要。
已提交但未备份的存储内存已映射 – 例如 – PROT_READ | PROT_WRITE
PROT_READ | PROT_WRITE
但访问它仍然会导致页面错误。 但是这个页面错误是由内核默默地处理的,通过支持实际的内存并返回执行,就好像什么都没发生一样。
即它是一个实现细节/优化,不会被过程本身注意到。
给出概念的细节:
已使用的Heap :根据最后的GC,活动对象占用的内存量
已提交 :已使用PROT_NONE以外的地址映射的地址范围。 由于懒惰的分配和寻呼,它们可能会或可能不会被物理或交换支持。
保留 :通过mmap
为特定内存池预映射的总地址范围。
保留提交差异由PROT_NONE
映射组成,保证不被物理内存支持
居民 :目前在物理ram中的页面。 这意味着代码,堆栈,已提交的内存池的一部分,也包含最近被访问的部分mmaped文件,以及JVM控制之外的分配。
虚拟 :所有虚拟地址映射的总和。 涵盖承诺,保留内存池,但也映射文件或共享内存。 这个数字很少提供信息,因为JVM可以预先保留非常大的地址范围或者mmap大文件。