文章目录
- 一、什么是内存溢出(OOM)
- [二、常见导致 OOM 的原因](#二、常见导致 OOM 的原因)
-
- [1. 一次读取太多数据](#1. 一次读取太多数据)
- [2. 本地缓存无限增长](#2. 本地缓存无限增长)
- [3. 内存泄漏](#3. 内存泄漏)
- [4. JVM 堆配置过小](#4. JVM 堆配置过小)
- [三、通过 Demo 快速复现 OOM(含命令)](#三、通过 Demo 快速复现 OOM(含命令))
- [四、使用 MAT 分析 Dump](#四、使用 MAT 分析 Dump)
-
- [1. 打开 Dump 文件](#1. 打开 Dump 文件)
- [2. 查看 Leak Suspects Report](#2. 查看 Leak Suspects Report)
- [3. Histogram:定位哪些类占用最多内存](#3. Histogram:定位哪些类占用最多内存)
- [4. 最关键:Dominator Tree](#4. 最关键:Dominator Tree)
- [5. Path to GC Roots:确认是谁"抓住"对象](#5. Path to GC Roots:确认是谁“抓住”对象)
- [五、修复 OOM 的常见方式](#五、修复 OOM 的常见方式)
- 六、总结
在 Java 服务运行过程中,OutOfMemoryError(简称 OOM)是最常见也最棘手的问题之一。它可能导致接口大量超时、服务卡死或频繁重启。要定位 OOM,我们通常需要经历下面三个步骤:
- 复现 OOM
- 生成 Heap Dump
- 使用 MAT 分析 Dump,找到真正的内存占用源头
一、什么是内存溢出(OOM)
JVM 在需要分配内存时,如果堆、元空间或直接内存耗尽,就会抛出 OOM。常见类型包括:
Java heap space:堆空间不足GC overhead limit exceeded:长时间 Full GC 效果仍然不好Metaspace:类元数据过多Direct buffer memory:堆外内存耗尽
无论哪种类型,本质都是内存资源无法满足当前分配请求。
二、常见导致 OOM 的原因
最主要的几个场景:
1. 一次读取太多数据
例如一次性加载整张大表、大文件。
2. 本地缓存无限增长
如静态 List、Map 无限追加对象,没有上限。
3. 内存泄漏
对象已无业务用途,但仍被引用导致无法被 GC 回收。
常见泄漏点包括:
- ThreadLocal 未 remove
- 静态集合持有大量对象
- 单例对象持有上下文数据
4. JVM 堆配置过小
堆限制远低于实际业务需求。
三、通过 Demo 快速复现 OOM(含命令)
为了快速演示 OOM,从而获取 Dump,我们可以通过一个小 Demo 服务来制造 OOM。
假设应用提供了一个接口 /cacheOrders,每次请求会往静态集合中塞入大对象。

第一步:启动服务并开启 OOM 自动 Dump
bash
java -Xms200m -Xmx200m \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/opt/data/dump \
-jar oom-1.0-SNAPSHOT.jar
参数解释:
-Xms200m -Xmx200m:将堆固定为 200MB,便于快速触发 OOM-XX:+HeapDumpOnOutOfMemoryError:发生 OOM 时自动生成 Dump-XX:HeapDumpPath:Dump 输出目录
第二步:循环请求接口造数据
bash
while true; do
curl "http://192.168.121.140:8080/cacheOrders?count=1000" >/dev/null 2>&1
done
这一行脚本会不断发送请求,让服务器将大量数据塞入静态缓存。
堆只有 200MB,很快会出现:
java.lang.OutOfMemoryError: Java heap space
同时在 /opt/data/dump 下生成 .hprof 文件。
到这里,复现 OOM 的过程就完成了,接下来进入最关键的环节------Dump 分析。
四、使用 MAT 分析 Dump
Heap Dump 是定位 OOM 的关键依据。Eclipse Memory Analyzer(MAT)是最常用的分析工具。
下面是从导入 dump 到找到问题的完整步骤。
1. 打开 Dump 文件
打开 MAT → File → Open Heap Dump

选择 .hprof 文件,MAT 会开始构建索引(大型 dump 会稍慢)。
打开后会自动显示概览页面。
2. 查看 Leak Suspects Report
在首页点击:
Leak Suspects Report

MAT 会自动生成一份内存使用情况的总结,包括:
- 哪些对象占用最多内存
- 是否存在可疑的泄漏路径
- 哪些集合膨胀严重
- 具体的引用链关系图(有图形化展示)
通常,一个静态集合导致的 OOM 会在这里直接出现提示。

3. Histogram:定位哪些类占用最多内存

左侧点击:
Histogram
按 "Retained Heap" 排序,你通常会看到类似结构:
com.example.Order 500,000 instances
java.util.ArrayList 占用巨大
byte[] 占用大量空间
Histogram 可帮助你快速发现哪些对象数量最多、体积最大。

4. 最关键:Dominator Tree
点开:
Dominator Tree
这是定位泄漏最核心的工具。
按 "Retained Heap" 排序后,你会看到占用最多的对象及其引用链。


这说明对象被静态集合长久持有,无法回收,是最典型的 OOM 场景。
5. Path to GC Roots:确认是谁"抓住"对象
右键某个大对象 → Path To GC Roots
MAT 会展示:
-
是哪个单例持有
-
是哪个静态字段持有

-
哪段缓存没有释放
结合引用链,你就可以定位到代码具体位置。
此时即可回到 IDE 里检查业务逻辑。
五、修复 OOM 的常见方式
定位到泄漏位置或增长点后,一般从以下方向修复:
- 限制缓存大小(推荐 Caffeine)
- 为静态集合加清理机制
- 避免一次性加载大量数据
- ThreadLocal 使用完后 remove
- 调整 JVM 堆配置,给足运行空间
- 优化数据结构和对象生命周期
修复后建议进行一次压测,确认堆使用是否稳定。
六、总结
Java OOM 排查并不复杂,只要记住以下步骤:
-
启动 JVM 时开启 OOM Dump
-
复现问题并生成
.hprof文件 -
使用 MAT 分析 Dump:
- Leak Suspects Report
- Histogram
- Dominator Tree(最重要)
-
根据引用链定位到具体代码
-
修复并验证效果