应优先通过jstat监控ou/mu持续上涨及unloaded为0定位内存泄漏,配合heapdumponoutofmemoryerror生成堆转储,用mat的dominator tree分析retained heap,重点排查threadlocal、静态集合及动态代理类加载器持有问题。

java.lang.OutOfMemoryError: Java heap space 怎么快速定位泄漏点
堆溢出最常见,但不能一上来就加 -Xmx —— 很多时候是对象没被回收,不是空间真不够。关键看是不是“用着用着才崩”,而不是一启动就挂。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 启动时必须加
-XX:+HeapDumpOnOutOfMemoryError,最好再加-XX:HeapDumpPath=/tmp/heap.hprof指定路径,避免 dump 生成失败或找不到文件 - 别等 OOM 才分析:用
jstat -gc <pid></pid>观察OU(老年代使用率)是否持续上涨且 GC 后不下降,这是典型内存泄漏信号 - 用 Eclipse MAT 打开 hprof 后,优先看 Dominator Tree,按 “Retained Heap” 排序,重点关注自己包名下的类;如果
java.util.ArrayList或byte[]占比异常高,大概率是缓存没清理、日志堆积、或大对象反复创建 - 容易踩的坑:误把
ThreadLocal引用的对象当“局部变量”——它实际绑定在线程上,线程池复用时极易造成泄漏;还有静态集合(如static Map<string object></string>)忘了做 size 限制或 LRU 清理
java.lang.OutOfMemoryError: Metaspace 是不是类太多?怎么验证
Metaspace 溢出 ≠ 你写的类多,而是类加载器卸不掉 —— 尤其在热部署、OSGi、Spring Boot DevTools、或用了 CGLIB/ASM 动态代理的场景下,旧 classloader 被新 loader 替换后,老类元数据还赖在 native memory 里。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 先确认是不是运行中增长:用
jstat -gc <pid></pid>看MU(Metaspace 使用量),配合jstat -compiler <pid></pid>看已加载类数(loaded)和是否发生过类卸载(unloaded)。若unloaded长期为 0,说明类根本没被释放 - 加参数
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps启动,观察 GC 日志里有没有类似Unloading class com.example.GeneratedProxy的记录 - 不要盲目调大
-XX:MaxMetaspaceSize:它只是延缓问题;真正要查的是谁持有了 classloader 引用(比如未关闭的 JDBC 连接、静态 ThreadLocal、未注销的 MBean) - 示例陷阱:Spring AOP 默认用 CGLIB 代理,每次
@Transactional方法被调用都可能生成新代理类;若事务方法在循环里高频执行,Metaspace 会线性增长
堆转储(heap dump)文件太大打不开怎么办
一个 4GB 的 heap.hprof 用 MAT 直接双击打开,大概率卡死或 OOM —— 不是工具不行,是默认配置太保守。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- MAT 启动前改配置:编辑
MemoryAnalyzer.ini,把-Xmx改成至少-Xmx6g(需机器有足够物理内存),否则 MAT 自己先崩 - 别全量分析:用
jmap -histo <pid></pid>快速看对象数量 TOP 20,命令输出里找instance数异常高的类名,再针对性用 MAT 的 “Merge Shortest Paths to GC Roots” 查引用链 - 更轻量替代方案:用
jcmd <pid> VM.native_memory summary</pid>看整体内存分布;或用VisualVM的“类”标签页实时观察类加载趋势,比 dump 更快定位 Metaspace 问题 - 容易踩的坑:在容器环境(如 Kubernetes)里,
/tmp可能是 tmpfs,空间不足导致 dump 失败;应显式指定-XX:HeapDumpPath=/var/log/myapp/并确保目录可写、有空间
为什么加了 -XX:+HeapDumpOnOutOfMemoryError 却没生成 dump 文件
这个参数看似简单,但失效原因非常具体,不是“没生效”,而是被系统级限制拦住了。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 检查 JVM 是否以
root以外用户运行,而HeapDumpPath目录属主是 root 且无写权限 —— dump 是 JVM 进程自己写的,权限不对就静默失败 - 确认磁盘剩余空间 ≥ 当前堆最大值(
-Xmx):JVM 会尝试分配一块与堆同大的连续文件空间,哪怕你只用了 100MB,它也要预留 2GB 空间来写 dump - Linux 系统限制:检查
ulimit -c(core file size),某些发行版默认设为 0,会连带禁用 heap dump;临时修复:ulimit -c unlimited - OpenJDK 17+ 用户注意:该参数在某些构建版本中与
-XX:+UseZGC冲突,若用 ZGC,建议换用jmap -dump手动触发
复杂点在于,OOM 本身可能发生在内存极度紧张时,连写文件的 native buffer 都申请不到 —— 所以线上一定要提前配好,别等炸了才试。










