当运行与本机内存跟踪Java应用程序(纱)启用( -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了多少内存在不同的类别使用 .
我在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的内存?其余的去哪儿了?
1 回答
是的,除非另有说明,否则malloc / mmap是懒惰的 . 一旦访问页面,页面仅由物理内存支持 .
GC堆内存有效地被复制收集器或预置零(
-XX:+AlwaysPreTouch
)触及,因此受此影响'll always be resident. Thread stacks otoh aren' .为了进一步确认,您可以使用
pmap -x <java pid>
并使用NMT的虚拟内存映射的输出交叉引用各种地址范围的RSS .保留的内存已用
PROT_NONE
进行了编码 . 这意味着虚拟地址空间范围在内核的vma结构中具有条目,因此不会被其他mmap / malloc调用使用 . 但它们仍会导致页面错误以SIGSEGV的形式转发到进程,即访问它们是一个错误 .这对于将来可以使用的连续地址范围很重要,这反过来又简化了指针算法 .
承诺但未备份的存储内存已映射 - 例如 -
PROT_READ | PROT_WRITE
但访问它仍会导致页面错误 . 但是,内核通过用实际内存支持并返回执行就好像没有发生任何事情来静默处理该页面错误 .即它会被流程本身注意到 .
给出概念的细分:
Used Heap :根据上一个GC,活动对象占用的内存量
Committed :已使用PROT_NONE以外的其他内容映射的地址范围 . 由于延迟分配和分页,它们可能会或可能不会由物理或交换支持 .
Reserved :已通过
mmap
为特定内存池预映射的总地址范围 .保留 - 提交的差异由
PROT_NONE
映射组成,这些映射保证不受物理内存的支持Resident :当前处于物理ram中的页面 . 这意味着代码,堆栈,已提交内存池的一部分,但也包括最近访问过的mmaped文件的一部分以及JVM控制之外的分配 .
Virtual :所有虚拟地址映射的总和 . 涵盖已提交,保留的内存池以及映射文件或共享内存 . 这个数字很少提供信息,因为JVM可以提前预留非常大的地址范围或mmap大文件 .