遇到outofmemoryerror: java heap space,说明jvm堆内存已无法分配新对象,程序不可恢复,需立即止血、定位并根治;先用jstat、jmap确认是否真缺内存或存在泄漏,再通过heapdumponoutofmemoryerror等参数自动留证,最后用mat分析gc roots定位泄漏源头。

遇到 OutOfMemoryError: Java heap space,说明 JVM 堆内存已无法分配新对象,程序已处于不可恢复状态。这不是能靠 try-catch 捕获继续运行的异常,而是系统资源告警——必须立即止血、精准定位、代码根治。
先确认是不是真缺内存,而不是泄漏在悄悄吃掉空间
别急着加 -Xmx。很多情况是堆明明够用,但对象被意外持有无法回收:
- 用
jstat -gc <pid></pid>观察 OU(老年代使用量):如果每次 Full GC 后 OU 持续上升,基本可判定内存泄漏 - 用
jmap -histo <pid></pid>查数量异常多的类,比如byte[]、char[]、HashMap$Node或自定义 DTO 列表 - 若 EU(伊甸园)频繁满但 OU 增长缓慢,可能是短期对象太多,GC 频繁但没泄漏,需优化对象生命周期或调整新生代比例
让 JVM 出事时自动留证据
生产环境不能靠“等下次再看”,必须提前配置自动诊断能力:
- 启动参数加上
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/dump/,OOM 时自动生成.hprof文件 - 同步添加
-XX:+PrintGCDetails -Xloggc:/opt/logs/gc.log,记录 GC 行为,判断是否回收效率骤降 - 确保
/opt/dump/路径可写、有足够磁盘空间,避免 dump 失败却无提示
用 MAT 精准揪出泄漏源头
拿到 .hprof 文件后,关键不是看谁占内存多,而是看“谁不让它被回收”:
- 用 Eclipse MAT 打开,先看 Leak Suspects Report,它会自动标出高嫌疑对象组
- 切换到 Dominator Tree,按 Retained Heap 排序,重点关注
ArrayList.elementData、ThreadLocalMap、ConcurrentHashMap等容器类 - 对可疑对象右键 → Path to GC Roots → exclude weak/soft references,只保留强引用链,就能看到是哪个静态字段、单例或线程池里的 ThreadLocal 在长期持有着
常见泄漏点和对应修复方式
以下写法看似无害,实则极易堆积对象:
-
静态集合无界增长:如
static Map<string list>> cache = new HashMap();</string>—— 改用 Guava Cache 并设置maximumSize和expireAfterWrite -
ThreadLocal 未清理:在线程池复用场景下,
threadLocal.set(...)后忘记remove()—— 必须在 finally 块中显式调用remove() -
大文件流/连接未关闭:如
FileInputStream、Connection、ResultSet—— 使用 try-with-resources,或确保在 finally 中 close -
日志误打全量数据:如
log.info("orders: {}", orderList),而 list 含数万条 —— 改为打数量或 ID 列表,避免 toString() 触发整棵树加载
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











