OOM实践及相应的排查方案
prerequisite
| 使用到的工具 | 备注 |
|---|---|
| GCViewer | 用于分析gc日志 |
| MAT | 用于分析堆转储文件 |
造成 OOM 的典型代码和排查方案(事前型的方案)
无限往List添加对象(堆溢出)
public class OomDemoApplication {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1024 * 1024]); // 每次1MB
}
}
}
方案
- 配置OOM时转储堆文件
配置的jvm options参数:
-Xms1024m
-Xmx2048m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/nbk/var/log/
-Xloggc:/home/nbk/var/log/gc.log
- 运行后抛出OOM异常,并生成堆转储文件和gc日志文件
OOM异常
堆转储文件*.hprof 和 gc日志文件gc.log

- 使用GCViewer分析gc日志文件
java -jar ./gcviewer-1.36.jar
打开刚刚生成的gc.log文件
丢给AI帮我们分析一下:
类似的不懂的指标都可以给AI,帮我们分析(多用几个AI,互相验证,防止幻觉)
分析结果:
Click the link to view conversation with Kimi AI Assistant https://www.kimi.com/share/19c9d675-a9d2-86ba-8000-00006a3a5b8d
可能很多人想要把AI生成的表格一键导出出来,我做了个油猴脚本,比较简陋,朋友们可以试试:表格一键导出
- 使用MAT分析堆转储文件
./mat/MemoryAnalyzer
可以直接看LeakSupport分析报告
mat比较容易的就找到了问题所在:

The memory is accumulated in one instance of java.lang.Object[], loaded by <system class loader>, which occupies 1,072,714,568 (99.91%) bytes.
看dominator tree里更加清晰
有大数组对象一直没有释放,已经没有内存了,在怎么FGC也没有用了……
造成 OOM 的典型代码和排查方案(事中型的方案)
- 内存泄漏 - 静态集合持有引用
public class Cache {
private static Map<String, Object> map = new HashMap<>();
public static void add(String key, Object value) {
map.put(key, value); // 永不释放
}
public static void main(String[] args) throws InterruptedException {
while (true) {
add(UUID.randomUUID().toString(), new Object[1024 * 1024]);
TimeUnit.SECONDS.sleep(1);
}
}
}
- jps命令查看应用进程id
jps -l
- jmap命令查看各代内存分布
jhsdb jmap --heap --pid 12001
4. 实时GC监控
jstat -gcutil 12001 1000 5
## 每1s输出一次gc情况,累计5次
5. jmap手动dump出堆情况
jmap -dump:format=b,file=dump.hprof 12001
然后打开MAT工具像上文一样进行分析
发现是HashMap对象消耗掉了所有的内存