jcmd诊断内存隐患的关键是不触发STW、不增开销、不改配置;需先用jcmd -l查PID并确认NMT已启用(-XX:NativeMemoryTracking=summary),再通过VM.native_memory summary毫秒级识别[thread]/[class]/[internal]异常增长,辅以Thread.print和GC.class_histogram交叉验证,禁用GC.run与GC.heap_dump保障生产安全。

用 jcmd 诊断内存隐患,关键在于“不触发 STW、不增开销、不改配置”。它本身是 JVM 内置通道,命令执行快、资源消耗低,只要提前开启 NMT(Native Memory Tracking),就能在不中断业务的前提下定位堆外内存异常、线程失控、元数据膨胀等典型隐患。
第一步:确认进程并检查 NMT 是否就绪
先用 jcmd -l 列出所有 Java 进程,找到目标 PID:
- 输出第二列是 PID,第三列是主类或 jar 路径;若第三列为空,可查
/proc/<pid>/cmdline确认启动方式 - 执行 jcmd <pid> VM.native_memory summary,如果返回 “Native memory tracking is not enabled”,说明未启用 NMT,需重启加
-XX:NativeMemoryTracking=summary - 已启用时,summary 命令毫秒级返回,不会卡住进程,适合高频巡检
第二步:聚焦三类高危内存信号
重点关注 native 内存中增长异常的模块,它们往往比堆泄漏更隐蔽:
- [thread] 持续上升 → 线程数失控(如虚拟线程未正确 close、线程池未设界、定时任务重复注册)
- [internal] 显著偏高或持续增长 → 虚拟线程元数据泄漏、JIT 编译缓存膨胀、DirectByteBuffer Cleaner 积压
- [class] 异常高(尤其搭配动态代理/Groovy/OSGi)→ 类加载器泄漏,常见于热部署或插件化场景
第三步:配合轻量命令交叉验证
单看 NMT 不够,需用其他 jcmd 子命令快速佐证:
立即学习“Java免费学习笔记(深入)”;
-
jcmd <pid> VM.native_memory detail(慎用):仅在低峰期执行,查看 malloc 调用栈,确认是否大量来自
Unsafe_AllocateMemory或DirectByteBuffer -
jcmd <pid> Thread.print:比 jstack 更稳定,能捕获 IN_NATIVE 卡死线程;搜
java.lang.VirtualThread和carrier=null可发现挂起的虚拟线程 - jcmd <pid> GC.class_histogram:不 dump 堆,秒出类实例数量 TOP 20,快速识别是否某类对象暴增(如 ByteBuf、FutureTask)
第四步:生产环境安全操作建议
避免踩坑,守住“零侵入”底线:
- 禁用
jcmd <pid> GC.run和GC.heap_dump—— 它们可能引发 Full GC 或长时间暂停,不符合“不影响生产”前提 - NMT detail 模式性能开销达 5–10%,只在问题复现时临时启用,排查后无需重启即可用
VM.native_memory shutdown关闭 - 容器环境务必确认挂载了
/proc,且 JDK 版本 ≥ 8u212 并启用-XX:+UseContainerSupport,否则 jcmd 可能返回空或权限错误

















