nmt用于追踪jvm自身非堆本地内存,必须启动时启用summary或detail级别;重点关注thread、metaspace、internal、codecache等模块的committed增长,结合baseline/diff和调用栈定位泄漏源头。

-XX:NativeMemoryTracking(简称 NMT)是 JVM 提供的原生内存诊断能力,用于跟踪 JVM 自身(非 Java 堆)所使用的本地内存,包括线程栈、CodeCache、GC 相关结构、JIT 编译器、类元数据(Metaspace)、内部缓冲区等。它不监控堆内存或 Java 对象,而是聚焦 JVM 进程在操作系统层面申请的 native memory,这对排查“Java 堆没满但进程 RSS 持续暴涨”“OOM Killer 杀进程”“容器内存超限被驱逐”等典型问题非常关键。
启用与基础检查
必须在 JVM 启动时开启,运行中无法动态激活:
- 添加参数:
-XX:NativeMemoryTracking=summary(轻量级,只统计各模块总量)或更详细的-XX:NativeMemoryTracking=detail - 建议搭配
-Xlog:nmt(Java 10+)或-XX:+PrintNMTStatistics(旧版)确保启动成功并记录日志 - 验证是否生效:运行
jcmd <pid> VM.native_memory summary</pid>,若返回各分类内存用量则说明已启用
定位内存持续增长的模块
定期对比不同时间点的 NMT 报告,重点关注“增长量大且不回落”的分类:
- 执行命令获取快照:
jcmd <pid> VM.native_memory summary scale=MB</pid> - 间隔数分钟或业务压力周期后再次执行,用文本工具比对两次输出中各模块的“committed”值变化
- 重点怀疑对象:Thread(线程数异常上升或栈大小配置过大)、Metaspace(类加载未卸载,如热部署/OSGi/反射生成类)、Internal(JVM 内部缓存,如 G1 的 remembered set、ZGC 的 metadata)、CodeCache(JIT 编译方法过多且未清理)
深入到调用栈级别(仅 detail 模式支持)
当 summary 显示某模块明显增长,需进一步下钻定位具体分配源头:
- 执行:
jcmd <pid> VM.native_memory detail scale=KB</pid> - 查看输出中类似
[0x00007f8a1c000000] allocation@23456 (2048KB)的行,其后的括号内为分配大小,前面地址可结合perf或pstack辅助分析 - 更实用的是关注
Virtual Memory Map区域下的malloc/mmap分配点,例如:malloc=123456KB #12345 calls from [libjvm.so+0xabcde],这提示大量小块 malloc 来自 JVM 的某个内部函数 - 常见线索:
Thread下出现大量pthread_create调用 → 线程池未限制;Metaspace下ClassLoaderData分配激增 → 类加载器泄漏;Internal中G1RemSet持续增长 → 大对象跨代引用过多
结合其他工具交叉验证
NMT 本身不提供实时火焰图或自动归因,需联动外部工具缩小范围:
- 确认线程数暴增:
ps -T -p <pid> | wc -l</pid>与jstack <pid> | grep 'java.lang.Thread' | wc -l</pid>对比,看是否存在大量 NEW/RUNNABLE 线程 - 检查 Metaspace 实际使用:
jstat -gc <pid></pid>查看MU(Metaspace 使用量)与MC(最大容量),再用jmap -clstats <pid></pid>看类加载器数量及每个加载器加载的类数 - 观察系统级内存:
pmap -x <pid></pid>查 RSS 总量和大内存映射段,/proc/<pid>/smaps</pid>中搜索Size:和MMUPageSize:找出大块匿名映射来源
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











