nmt无法监控c++内存泄漏,因其仅跟踪jvm内部封装的native调用(如os::malloc),而c++直接调用new/malloc或第三方jni库的分配完全脱离jvm管控,故jcmd vm.native_memory不可见。

NMT 不能监控 C++ 内存分配,只能跟踪 JVM 自身调用的 native 内存(如 DirectByteBuffer、线程栈、CodeCache 等),第三方 native 库(包括 OpenSSL、自定义 JNI 库、C++ new/malloc)完全不可见。
为什么 jcmd VM.native_memory 看不到你的 C++ 内存泄漏
NMT 的设计边界非常明确:它只 hook JVM 内部的 malloc/free 调用点,比如 os::malloc、os::realloc 这类 HotSpot 自封装的内存分配函数。而你写的 C++ 代码里直接调用 malloc、new 或通过 JNI 调用的第三方 so/dll,JVM 根本不介入,也就不会被记录。
常见误判场景:
- 用
System.loadLibrary("mylib")加载了自研 C++ 库,发现 RES 持续上涨 → NMT 的Internal或Other区域几乎不变 → 实际是 C++ 层泄漏 - Netty 使用
EpollEventLoop+ JNI 调用 epoll_wait →Thread和Internal会上涨,但具体 epoll 相关 buffer 分配不计入调用栈(除非你启用了-XX:NativeMemoryTracking=detail并恰好触发了 JVM 封装路径) - 用 JNI 调用 OpenCV
cv::Mat::create()→ 全部逃逸在 NMT 视野之外
启用 NMT 后该关注哪几块内存区域
启动时必须加 -XX:NativeMemoryTracking=summary(基础)或 -XX:NativeMemoryTracking=detail(带调用栈,推荐诊断期使用);否则 jcmd VM.native_memory 会报错或返回空。
重点关注以下四类(按排查优先级排序):
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
-
Internal:最可疑区域,包含DirectByteBuffer分配、JNI 全局引用表、JVMTI agent 分配等。泄漏常表现为该值随时间线性增长 -
Thread:每个线程默认占 ~1MB(-Xss控制),若看到该值 >200 * -Xss,说明线程数异常膨胀(如 Netty worker 线程未复用、定时任务没 shutdown) -
Class:类加载器泄漏典型表现,尤其在热部署、OSGi、Spring Boot DevTools 场景下,Metaspace接近-XX:MaxMetaspaceSize但Class行持续涨 -
Other:兜底分类,含 JVM 内部结构、命令行参数解析缓存等。突然飙升往往指向 JVM bug 或极端配置(如超大-XX:CompilerThreadStackSize)
如何用 baseline + diff 定位真实增长源
光看 summary 是静态快照,无法判断“谁在 leak”。必须配合基线比对:
- 应用刚启动、完成初始化后立刻执行:
jcmd <pid> VM.native_memory baseline</pid> - 运行一段时间(比如 30 分钟)后执行:
jcmd <pid> VM.native_memory summary scale=MB</pid>,此时输出会显示各区域相比 baseline 的增量(+/- 值) - 若想看具体是谁分配的,需提前用
-XX:NativeMemoryTracking=detail,再执行:jcmd <pid> VM.native_memory detail.diff scale=MB</pid>,它会列出新增分配的调用栈(例如java.nio.Bits.reserveMemory→DirectByteBuffer.<init></init>)
⚠️ 注意:detail 模式有约 5%~10% 性能开销,生产环境慎用;且调用栈只记录 JVM 自身路径,不包含你 JNI 函数里的 malloc。
当 NMT 显示正常但 RES 仍在涨,下一步做什么
这是最典型的“NMT 无能为力”信号,意味着泄漏源在 JVM 外部:
- 用
pstack <pid></pid>看线程数是否异常多(> 1000?) - 用
cat /proc/<pid>/maps | grep -E "(rw.-|anon)" | awk '{sum += $3} END {print sum/1024 " MB"}'</pid>粗略估算匿名映射区大小(含 malloc 分配) - 对 C++ 库启用
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2+MALLOC_CONF=prof:true,prof_prefix:jeprof.out,再用jeprof分析 - 若用了 JNI,检查是否漏调
DeleteGlobalRef、ReleaseStringUTFChars等释放逻辑
真正棘手的堆外问题,往往不是 JVM 没记,而是它根本没资格记——那部分内存,从来就不归它管。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










