java.lang.outofmemoryerror: java heap space 表明jvm堆内存耗尽且gc无法释放足够空间,主因是内存泄漏(如静态集合未清理)或堆容量不足(如批量操作未分页),需结合jstat监控与mat分析堆转储定位根因。

看到 java.lang.OutOfMemoryError: Java heap space,说明堆内存真的撑不住了。这不是系统物理内存不够,而是 JVM 堆空间(由 -Xmx 控制)被耗尽,且 GC 也腾不出足够空间。关键不在于“加内存”,而在于搞清:是代码在悄悄囤货(泄漏),还是业务真把堆填满了(容量不足)?
先看现象,分清两类典型走势
- 如果堆使用率随时间缓慢、持续上升,Full GC 后老年代占用不降反升 → 倾向内存泄漏
- 如果某次批量导出、大促压测或配置变更后,堆瞬间飙高并直接 OOM → 更可能是峰值容量不足
用 jstat 快速验证 GC 状态
执行命令:
jstat -gc -h10 <pid> 2000</pid>
重点关注三列:
-
OU(Old Used):老年代已用空间。若持续逼近OC(Old Capacity),且多次 Full GC 后仍不下落,大概率有对象长期存活 -
FGC(Full GC 次数):陡增 +FGCT(Full GC 耗时)变长,说明 GC 已疲于奔命 -
GCT(总 GC 时间占比):若超过 10%,需警惕 GC overhead 问题
抓堆转储,直击“谁占得最多”
确保启动时已加参数:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/dump/
OOM 发生后会自动生成 .hprof 文件。用 Eclipse MAT 打开,重点看:
- Dominator Tree → 按 Retained Heap 降序排列,找顶部几个类(不是 Shallow Heap)
-
Histogram → 查看
java.lang.String、byte[]、ArrayList或你自己的业务实体类实例数是否异常多 - Leak Suspects Report → 自动生成可疑引用链,通常能一眼定位静态缓存、未清理的监听器或 ThreadLocal
顺着 GC Roots 追强引用链
在 MAT 中对可疑对象点右键 → Path to GC Roots → 选 Exclude weak/soft references。常见泄漏源头包括:
- 静态集合(如
public static Map<string object> cache = new HashMap()</string>)没做大小限制或过期清理 - Spring Bean 中
@Scope("singleton")却持有HttpServletRequest或线程上下文等请求级对象 - 使用
ThreadLocal后未调用remove(),尤其在线程池场景下导致对象跨请求残留 - 数据库连接、文件流、网络 Socket 未用 try-with-resources 或未显式 close
代码和配置层面快速检查项
- 批量查询是否用了
SELECT * FROM huge_table?必须加LIMIT或改用游标分页 - 缓存是否无界?优先用 Caffeine 并配置
maximumSize和expireAfterWrite - 是否在循环里反复
new byte[1024*1024]或解析超大 JSON?考虑流式处理或对象复用 - JVM 参数是否合理?建议设
-Xms=-Xmx避免扩容抖动,并始终开启-XX:+HeapDumpOnOutOfMemoryError
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











