JVM本地内存的使用监控情况
NMT,全称为Native Memory Tracking,是Java 8u40版本引入的一项功能,用于跟踪JVM本身在本地内存中的内存使用情况。
NMT 不支持跟踪非 JVM 代码的内存分配,需要使用操作系统支持的工具来检测本地代码中的内存泄漏。
启用 NMT 会导致 JVM 性能下降 5%~10%,并且NMT的内存使用将在所有malloc内存中添加2个机器字作为malloc头。
NMT跟踪的内存
本机内存跟踪内存类别
类别 | 说明 |
---|---|
Java Heap | The heap where your objects live 对象所在的堆的内存使用情况 |
Class | Class meta data 元数据区使用的内存情况 |
Code | Generated code JIT产生的汇编指令所占的空间情况 |
GC | data use by the GC, such as card table 由GC使用的内存情况,如card table |
Compiler | Memory used by the compiler when generating code 编译器在生成代码时使用的内存情况 |
Symbol | Symbols 符号,如同String table和常量池 |
Memory Tracking | Memory used by NMT itself NMT本身使用的内存 |
Pooled Free Chunks | Memory used by chunks in the arena chunk pool chunks 池中块使用的内存 |
Shared space for classes | Memory mapped to class data sharing archive 内存映射到类数据共享归档 |
Thread | Memory used by threads, including thread data structure, resource area and handle area and so on. 线程使用的内存,包括线程数据结构、资源区和句柄区等。 |
Thread stack | Thread stack. It is marked as committed memory, but it might not be completely committed by the OS 线程栈。 它被标记为已提交内存,但操作系统可能不会完全提交它 |
Internal | Memory that does not fit the previous categories, such as the memory used by the command line parser, JVMTI, properties and so on. 不属于前几类的内存,如命令行解析器、JVMTI、属性等使用的内存。 |
Unknown | When memory category can not be determined.Arena: When arena is used as a stack or value objectVirtual Memory: When type information has not yet arrived 当无法确定内存类别时: 当arena 被用作堆栈或值对象时虚拟内存: 当类型信息尚未统计到时 |
使用 NMT 检测内存泄漏
启动Java程序时,需要增加下列命令行选项:
-XX:NativeMemoryTracking=summary
或-XX:NativeMemoryTracking=detail
,以summary或detail跟踪方式启动 JVM。建立早期基线–使用 NMT base功能获得基线,以便在开发和维护期间进行比较,方法是运行:
jcmd <pid> VM.native_memory baseline
使用下面命令, 监控内存变化:
jcmd <pid> VM.native_memory detail.diff
如果应用程序泄漏了少量内存,则需要运行一段时间才能显示出来。
如何监控虚拟机内部内存
Arena 是使用 malloc 分配的一大块内存。在退出作用域或离开代码区域时,会从这些内存块中批量释放内存。这些内存块可在其他子系统中重复使用,以保存临时内存,例如线程前分配。Arena 的 malloc 策略可确保无内存泄漏。因此,Arena 将作为一个整体而非单个对象进行跟踪。有些初始内存是无法跟踪的。
与 jcmd
实用程序一起使用时,可以查询空间情况。
下文将介绍如何获取 NMT 的摘要或详细数据,以及如何解释示例输出。
获取摘要报告
前提:使用命令行选项 -XX:NativeMemoryTracking=summary
启动 JVM。
使用命令:
jcmd <pid> VM.native_memory summary
结果示例:
Total: reserved=664192KB, committed=253120KB <--- total memory tracked by Native Memory Tracking
- Java Heap (reserved=516096KB, committed=204800KB) <--- Java Heap
(mmap: reserved=516096KB, committed=204800KB)
- Class (reserved=6568KB, committed=4140KB) <--- class metadata
(classes #665) <--- number of loaded classes
(malloc=424KB, #1000) <--- malloc'd memory, #number of malloc
(mmap: reserved=6144KB, committed=3716KB)
- Thread (reserved=6868KB, committed=6868KB)
(thread #15) <--- number of threads
(stack: reserved=6780KB, committed=6780KB) <--- memory used by thread stacks
(malloc=27KB, #66)
(arena=61KB, #30) <--- resource and handle areas
- Code (reserved=102414KB, committed=6314KB)
(malloc=2574KB, #74316)
(mmap: reserved=99840KB, committed=3740KB)
- GC (reserved=26154KB, committed=24938KB)
(malloc=486KB, #110)
(mmap: reserved=25668KB, committed=24452KB)
- Compiler (reserved=106KB, committed=106KB)
(malloc=7KB, #90)
(arena=99KB, #3)
- Internal (reserved=586KB, committed=554KB)
(malloc=554KB, #1677)
(mmap: reserved=32KB, committed=0KB)
- Symbol (reserved=906KB, committed=906KB)
(malloc=514KB, #2736)
(arena=392KB, #1)
- Memory Tracking (reserved=3184KB, committed=3184KB)
(malloc=3184KB, #300)
- Pooled Free Chunks (reserved=1276KB, committed=1276KB)
(malloc=1276KB)
- Unknown (reserved=33KB, committed=33KB)
(arena=33KB, #1)
查询详细报告
前提:使用命令行选项 -XX:NativeMemoryTracking=detail
启动 JVM。
使用命令:
jcmd <pid> VM.native_memory detail
结果示例:
Virtual memory map:
[0x8f1c1000 - 0x8f467000] reserved 2712KB for Thread Stack
from [Thread::record_stack_base_and_size()+0xca]
[0x8f1c1000 - 0x8f467000] committed 2712KB from [Thread::record_stack_base_and_size()+0xca]
[0x8f585000 - 0x8f729000] reserved 1680KB for Thread Stack
from [Thread::record_stack_base_and_size()+0xca]
[0x8f585000 - 0x8f729000] committed 1680KB from [Thread::record_stack_base_and_size()+0xca]
[0x8f930000 - 0x90100000] reserved 8000KB for GC
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555]
[0x8f930000 - 0x90100000] committed 8000KB from [PSVirtualSpace::expand_by(unsigned int)+0x95]
[0x902dd000 - 0x9127d000] reserved 16000KB for GC
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555]
[0x902dd000 - 0x9127d000] committed 16000KB from [os::pd_commit_memory(char*, unsigned int, unsigned int, bool)+0x36]
[0x9127d000 - 0x91400000] reserved 1548KB for Thread Stack
from [Thread::record_stack_base_and_size()+0xca]
[0x9127d000 - 0x91400000] committed 1548KB from [Thread::record_stack_base_and_size()+0xca]
[0x91400000 - 0xb0c00000] reserved 516096KB for Java Heap <--- reserved memory range
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x190] <--- callsite that reserves the memory
[0x91400000 - 0x93400000] committed 32768KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x3e8] <--- committed memory range and its callsite
[0xa6400000 - 0xb0c00000] committed 172032KB from [PSVirtualSpace::expand_by(unsigned int)+0x95] <--- committed memory range and its callsite
[0xb0c61000 - 0xb0ce2000] reserved 516KB for Thread Stack
from [Thread::record_stack_base_and_size()+0xca]
[0xb0c61000 - 0xb0ce2000] committed 516KB from [Thread::record_stack_base_and_size()+0xca]
[0xb0ce2000 - 0xb0e83000] reserved 1668KB for GC
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555]
[0xb0ce2000 - 0xb0cf0000] committed 56KB from [PSVirtualSpace::expand_by(unsigned int)+0x95]
[0xb0d88000 - 0xb0d96000] committed 56KB from [CardTableModRefBS::resize_covered_region(MemRegion)+0xebf]
[0xb0e2e000 - 0xb0e83000] committed 340KB from [CardTableModRefBS::resize_covered_region(MemRegion)+0xebf]
[0xb0e83000 - 0xb7003000] reserved 99840KB for Code
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555]
[0xb0e83000 - 0xb0e92000] committed 60KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x3e8]
[0xb1003000 - 0xb139b000] committed 3680KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x37a]
[0xb7003000 - 0xb7603000] reserved 6144KB for Class
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555]
[0xb7003000 - 0xb73a4000] committed 3716KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x37a]
[0xb7603000 - 0xb760b000] reserved 32KB for Internal
from [PerfMemory::create_memory_region(unsigned int)+0x8ba]
[0xb770b000 - 0xb775c000] reserved 324KB for Thread Stack
from [Thread::record_stack_base_and_size()+0xca]
[0xb770b000 - 0xb775c000] committed 324KB from [Thread::record_stack_base_and_size()+0xca]
查询跟踪变化报告
对于summary和detail级别的跟踪,可以在应用程序启动并运行后设置基线。设置baseline命令:
jcmd <pid> VM.native_memory baseline
查询不同级别变化报告命令
jcmd <pid> VM.native_memory summary.diff
jcmd <pid> VM.native_memory detail.diff
命令可以带上单位
jcmd <pid> VM.native_memory summary.diff scale=MB
summary级别变化报告结果示例
Total: reserved=664624KB -20610KB, committed=254344KB -20610KB <--- total memory changes vs. earlier baseline. '+'=increase '-'=decrease
- Java Heap (reserved=516096KB, committed=204800KB)
(mmap: reserved=516096KB, committed=204800KB)
- Class (reserved=6578KB +3KB, committed=4530KB +3KB)
(classes #668 +3) <--- 3 more classes loaded
(malloc=434KB +3KB, #930 -7) <--- malloc'd memory increased by 3KB, but number of malloc count decreased by 7
(mmap: reserved=6144KB, committed=4096KB)
- Thread (reserved=60KB -1129KB, committed=60KB -1129KB)
(thread #16 +1) <--- one more thread
(stack: reserved=7104KB +324KB, committed=7104KB +324KB)
(malloc=29KB +2KB, #70 +4)
(arena=31KB -1131KB, #32 +2) <--- 2 more arenas (one more resource area and one more handle area)
- Code (reserved=102328KB +133KB, committed=6640KB +133KB)
(malloc=2488KB +133KB, #72694 +4287)
(mmap: reserved=99840KB, committed=4152KB)
- GC (reserved=26154KB, committed=24938KB)
(malloc=486KB, #110)
(mmap: reserved=25668KB, committed=24452KB)
- Compiler (reserved=106KB, committed=106KB)
(malloc=7KB, #93)
(arena=99KB, #3)
- Internal (reserved=590KB +35KB, committed=558KB +35KB)
(malloc=558KB +35KB, #1699 +20)
(mmap: reserved=32KB, committed=0KB)
- Symbol (reserved=911KB +5KB, committed=911KB +5KB)
(malloc=519KB +5KB, #2921 +180)
(arena=392KB, #1)
- Memory Tracking (reserved=2073KB -887KB, committed=2073KB -887KB)
(malloc=2073KB -887KB, #84 -210)
- Pooled Free Chunks (reserved=2624KB -15876KB, committed=2624KB -15876KB)
(malloc=2624KB -15876KB)
Details级别变化报告结果示例
Details:
[0x01195652] ChunkPool::allocate(unsigned int)+0xe2
(malloc=482KB -481KB, #8 -8)
[0x01195652] ChunkPool::allocate(unsigned int)+0xe2
(malloc=2786KB -19742KB, #134 -618)
[0x013bd432] CodeBlob::set_oop_maps(OopMapSet*)+0xa2
(malloc=591KB +6KB, #681 +37)
[0x013c12b1] CodeBuffer::block_comment(int, char const*)+0x21 <--- [callsite address] method name + offset
(malloc=562KB +33KB, #35940 +2125) <--- malloc'd amount, increased by 33KB #malloc count, increased by 2125
[0x0145f172] ConstantPool::ConstantPool(Array<unsigned char>*)+0x62
(malloc=69KB +2KB, #610 +15)
...
[0x01aa3ee2] Thread::allocate(unsigned int, bool, unsigned short)+0x122
(malloc=21KB +2KB, #13 +1)
[0x01aa73ca] Thread::record_stack_base_and_size()+0xca
(mmap: reserved=7104KB +324KB, committed=7104KB +324KB)