nmt可追踪jvm非堆内存,需启动时指定summary或detail级别;summary用于日常监控,detail用于定位泄漏;重点关注thread、code、internal三类区域,结合baseline/diff和调用栈分析增长源头。

要真正看清 JVM 本地内存的去向,光看堆和元空间远远不够。NMT 的价值,就在于把 Thread、Code、Internal 这些“看不见摸不着”的非堆内存区域,变成可查、可比、可定位的数据。
必须在启动时开启,且选对级别
NMT 不是运行时开关,它必须随 JVM 一起启动。参数格式严格限定为:
-XX:NativeMemoryTracking=summary 或 -XX:NativeMemoryTracking=detail
不能写成 on、true 或放在 -jar 后面——JVM 会静默忽略。
- summary:适合生产环境日常监控,只显示各子系统总用量(如 Thread、Code、GC),开销小
- detail:能展示调用栈和分配位置,适合定位泄漏点,但带来约 5–10% 性能损耗
- 首次排查建议先用 summary 快速判断异常模块;确认问题后再重启启用 detail 深挖
重点关注三类高风险区域
执行 jcmd
- Thread:持续增长说明线程数失控或单个线程栈过大(比如 -Xss 设得太高,或递归过深)
- Code:对应 JIT 编译生成的机器码,若长期上涨可能因频繁动态类加载或大量 Lambda 触发编译膨胀
- Internal:DirectByteBuffer 分配、Netty 的池化内存、JNI 调用等都记入此项;异常增长大概率指向未释放的直接缓冲区
用 baseline + diff 抓住变化源头
NMT 不自动采样,必须人工建立参照系才能看出趋势:
- 服务刚启动、负载平稳时,立即执行:
jcmdVM.native_memory baseline - 运行一段时间(如压测后、或观察到 RES 上升时),执行:
jcmdVM.native_memory summary.diff scale=MB - 重点看 committed 列变动大的项,例如:
→ Thread committed +200MB → 查线程数是否翻倍
→ Internal committed +350MB → 结合代码检查 DirectByteBuffer.allocateDirect() 是否遗漏 clean()
结合 detail 模式定位具体代码位置
当 summary.diff 显示 Internal 或 Thread 异常增长,切换到 detail 模式:
- 执行 jcmd
VM.native_memory detail.scale=MB | grep -A10 -B5 "Internal" - 输出中会出现类似这样的调用链:
[0x0000ffff93e3acc0] Unsafe_AllocateMemory+0x48
[0x0000ffff9377a498] DirectByteBuffer.+0x120 - 这说明增长来自 DirectByteBuffer 构造;再结合应用日志或代码扫描,就能锁定具体类和方法










