堆外内存泄漏排查需先确认RES异常高于Xmx且堆GC正常,再启用NMT开启detail追踪,通过baseline对比定位[direct]/[class]/[thread]等增长项,最后结合Netty泄漏检测、Metaspace监控或线程数统计交叉验证。

堆外内存泄漏排查的关键在于跳出“只看堆”的惯性思维,转而聚焦操作系统层面的物理内存使用和 JVM 本地内存分配行为。它不抛 OutOfMemoryError: Java heap space,但进程 RES(常驻内存)会持续上涨,最终被系统 OOM Killer 杀掉。
第一步:确认是不是堆外泄漏
别急着查代码,先做现象判断:
- 用
top -p <pid>查看 RES 值,明显高于-Xmx设置(比如 Xmx=4G,RES=12G 且还在涨) - 同时运行
jstat -gc <pid>,发现老年代使用率低、GC 频次少、没有 Full GC,堆监控一切正常 - 容器或宿主机内存告警,但 JVM 内部指标(如 JMX 的 HeapMemoryUsage)没突破阈值
第二步:启用并验证 NMT(Native Memory Tracking)
NMT 是 HotSpot 内置最直接的堆外内存追踪工具,但必须启动时开启:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 重启应用,JVM 参数加:
-XX:NativeMemoryTracking=detail -XX:+UnlockDiagnosticVMOptions - 参数必须放在
java命令最前面,不能写在-jar后面,否则静默失效 - 启动后立刻执行:
jcmd <pid> VM.native_memory summary,看Other或Internal是否异常高
第三步:定位增长源头
通过基线对比,找出哪块内存在“偷偷长大”:
立即学习“Java免费学习笔记(深入)”;
- 负载平稳时建基线:
jcmd <pid> VM.native_memory baseline - 过 5–10 分钟再执行:
jcmd <pid> VM.native_memory detail.diff | grep -A5 -B5 "committed.*[0-9]M" - 重点关注这几项的增量:
[direct](DirectByteBuffer)、[class](Metaspace)、[thread](线程栈)、[arena](本地 malloc 区)
第四步:结合场景做针对性验证
不同增长项对应不同问题,需搭配辅助手段交叉印证:
- 若
[direct]增长快 → 检查 Netty:加-Dio.netty.leakDetection.level=PARANOID,看日志是否输出LEAK: ByteBuf.release() was not called - 若
[class]持续涨 → 检查动态代理或脚本引擎:用jstat -gcmetacapacity <pid>看 Metaspace 使用趋势,配合jcmd <pid> VM.class_hierarchy | head -50查高频生成类 - 若
[thread]显著上升 → 查线程数:ps -T -p <pid> | wc -l,再用jstack <pid> | grep "java.lang.Thread.State" | wc -l看实际活跃线程

















