目录
[场景1:Java heap space](#场景1:Java heap space)
[场景3:GC overhead limit exceeded](#场景3:GC overhead limit exceeded)
[1. 使用Arthas进行在线诊断](#1. 使用Arthas进行在线诊断)
[2. 内存泄漏的Breadcrumb策略](#2. 内存泄漏的Breadcrumb策略)
[3. 压力测试复现问题](#3. 压力测试复现问题)
一、什么是OOM?
OOM(Out Of Memory)即内存溢出,是Java开发中最常见的错误之一。当JVM内存不足以分配对象空间,并且垃圾收集器也无法回收足够内存时,就会抛出java.lang.OutOfMemoryError
错误。

常见的OOM错误类型包括:
-
java.lang.OutOfMemoryError: Java heap space
(堆内存不足) -
java.lang.OutOfMemoryError: Metaspace
(元空间不足) -
java.lang.OutOfMemoryError: GC overhead limit exceeded
(GC开销过大) -
java.lang.OutOfMemoryError: unable to create new native thread
(无法创建新线程)
二、OOM排查的整体思路
确认错误类型:首先查看OOM的具体错误信息,确定是哪种类型的内存溢出
收集现场信息:在OOM发生时尽可能多地收集系统状态信息
分析内存使用:通过工具分析内存使用情况
定位问题代码:找到导致内存泄漏或过度消耗的代码
修复与验证:修复问题并验证解决方案的有效性
三、OOM排查工具大全
- 命令行工具
jps - 查看Java进程
bash
jps -l
jstat - 监控内存和GC情况
bash
jstat -gcutil <pid> 1000 10 # 每1秒输出一次,共10次
jmap - 内存分析
bash
jmap -heap <pid> # 查看堆内存配置和使用情况
jmap -histo <pid> # 查看对象统计信息
jmap -dump:format=b,file=heap.hprof <pid> # 生成堆转储文件
jstack - 线程分析
bash
jstack <pid> > thread.txt
- 可视化工具
VisualVM:JDK自带的可视化监控工具
MAT (Memory Analyzer Tool):强大的堆转储文件分析工具
JProfiler:商业级性能分析工具
Arthas:阿里开源的Java诊断工具
四、实战:不同OOM场景的排查方法
场景1:Java heap space
典型表现:
java
java.lang.OutOfMemoryError: Java heap space
排查步骤:
- 增加JVM参数收集信息:
bash
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump.hprof
- 使用jmap手动生成堆转储文件:
bash
jmap -dump:format=b,file=heap.hprof <pid>
使用MAT分析堆转储文件:
查找占用内存最大的对象
查看对象的引用链
定位到具体的类和代码行
常见原因:
内存泄漏(对象被意外持有无法回收)
数据量确实过大(需要增加堆内存或优化程序)
场景2:Metaspace
典型表现:
java
java.lang.OutOfMemoryError: Metaspace
排查步骤:
查看元空间使用情况:
bash
jstat -gc <pid>
调整JVM参数收集更多信息:
bash
-XX:+TraceClassLoading -XX:+TraceClassUnloading
常见原因:
动态生成大量类(如CGLib动态代理)
元空间设置过小(适当增加-XX:MaxMetaspaceSize
)
类加载器泄漏
场景3:GC overhead limit exceeded
典型表现:
java
java.lang.OutOfMemoryError: GC overhead limit exceeded
排查步骤:
查看GC日志:
bash
-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
分析GC效率:
关注GC频率和耗时
观察每次GC后的内存回收情况
常见原因:
堆内存设置过小
存在内存泄漏导致GC无法有效回收内存
对象存活时间配置不当
五、高级排查技巧
1. 使用Arthas进行在线诊断
bash
# 启动Arthas
java -jar arthas-boot.jar
# 查看JVM内存情况
dashboard
# 监控方法调用
monitor -c 5 com.example.demo.Test testMethod
# 查看对象引用
vmtool --action getInstances --className java.lang.String --limit 10
2. 内存泄漏的Breadcrumb策略
定期(如每小时)执行jmap -histo:live <pid> > histo_$i.log
对比不同时间点的对象数量变化
找出异常增长的对象类型
3. 压力测试复现问题
使用JMeter或自定义脚本模拟高并发场景,配合以下JVM参数监控:
bash
-XX:+PrintGCDetails
-Xloggc:gc.log
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./oom_dump.hprof
六、预防OOM的最佳实践
-
合理设置JVM参数:
- 根据应用特点设置初始(-Xms)和最大(-Xmx)堆内存
- 新生代和老年代比例(-XX:NewRatio)
- 设置元空间大小(-XX:MaxMetaspaceSize)
-
代码层面优化:
- 避免大对象长期存活
- 及时关闭资源(数据库连接、文件流等)
- 谨慎使用静态集合
- 合理设计缓存策略
-
监控与告警:
- 实施JVM监控(如Prometheus + Grafana)
- 设置内存使用阈值告警
- 定期检查GC日志
-
定期演练:
- 模拟OOM场景进行演练
- 验证监控告警的有效性
- 测试团队对OOM的响应流程
七、真实案例分享
案例1:静态HashMap导致的内存泄漏
现象:应用运行几天后必现OOM
排查:
- 堆转储分析显示HashMap占用了80%内存
- 追溯发现是全局静态HashMap缓存用户数据但从未清理
- 随着用户量增加,HashMap不断增长
解决:
- 改用WeakHashMap或带过期策略的缓存
- 实现定期清理机制
案例2:动态代理类撑爆Metaspace
现象:高并发下频繁出现Metaspace OOM
排查:
- 发现Metaspace使用量持续增长
- 分析类加载日志发现大量动态代理类
- 确认是框架为每个请求生成新代理类
解决:
- 增加Metaspace大小
- 优化框架配置,启用代理类缓存
八、总结
OOM排查是Java开发者必备的技能,需要掌握:
- 理解JVM内存模型和各区域作用
- 熟练使用各种诊断工具
- 建立系统化的排查思路
- 积累常见场景的解决经验
- 重视预防和监控,而非仅事后补救
记住:好的开发者不是不会遇到OOM,而是能够快速定位和解决OOM问题。希望本文能帮助你在遇到OOM时不再恐慌,而是有条不紊地解决问题。