一、OOM问题类型与核心特征
Java内存溢出(OutOfMemoryError)本质是 JVM内存区域耗尽,根据错误信息可快速定位问题根源:
OOM类型 | 错误信息特征 | 常见场景 |
---|---|---|
堆内存溢出 | java.lang.OutOfMemoryError: Java heap space | 大对象分配、内存泄漏、缓存失控 |
元空间溢出 | java.lang.OutOfMemoryError: Metaspace | 动态生成类(反射/CGLIB)、类加载器泄漏 |
直接内存溢出 | java.lang.OutOfMemoryError: Direct buffer memory | NIO操作未释放、Netty堆外内存配置不当 |
GC Overhead超限 | java.lang.OutOfMemoryError: GC Overhead limit exceeded | 小对象高频分配、堆内存长期占满 |
线程栈溢出 | java.lang.OutOfMemoryError: Unable to create new native thread | 线程数超过系统限制(ulimit -u ) |
二、通用排查流程
1. 确认OOM类型
通过日志中的错误信息确定内存区域,例如:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
2. 获取内存快照(Heap Dump)
在JVM启动参数中添加自动转储配置(推荐):
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dump.hprof
HeapDumpOnOutOfMemoryError 是 Java 虚拟机的一个选项。当发生 OutOfMemoryError(内存溢出错误)时,设置了该选项后,Java 虚拟机会生成一个堆转储文件(heap dump),用于分析导致内存溢出的原因。当然还可以顺便指定文件生成的路径 (-XX:HeapDumpPath=<转储文件路径> ),不指定就是默认项目所在的根路径下。
若未提前配置,可对运行中的进程手动抓取:
jmap -dump:live,format=b,file=heap.hprof <PID>
编写一个测试类测试:
public class OOMTest {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
do {
User user = new User();
user.setName(UUID.randomUUID().toString());
userList.add(new User());
} while (true);
}
}
运行后可以看到出现了OOM,并且生成了一个dump文件在根目录下:
3. 分析堆转储文件
使用 VisualVM:
- 打开
.hprof
文件,生成内存泄漏报告(Leak Suspects)。
- 观察内存泄漏报告,发现User实例数量占用了80.6%,且占用了54.3%的内存。
- 到这基本上就可以确认问题了 ,其实就是main函数里面一直new User ,把内存用炸了,且报告中也有具体的代码行定位
三、分场景排查与解决
场景1:堆内存泄漏(Java heap space)
典型特征
- 老年代持续增长,Full GC后无法回收
- 堆转储中存在大量重复对象
排查步骤
-
定位泄漏对象
# 查看堆内存对象分布 jmap -histo:live <PID> | head -n 20
-
分析引用链
- 在MAT中搜索占内存最大的类,查看其GC Root引用。
- 常见根源:静态集合类(如
static HashMap
)、未关闭的资源(数据库连接)。
-
代码修复示例
// 错误代码:静态Map未清理导致内存泄漏 public class CacheManager { private static Map<String, Object> cache = new HashMap<>(); public static void add(String key, Object value) { cache.put(key, value); } } // 修复方案:增加清理逻辑或改用WeakHashMap public static void remove(String key) { cache.remove(key); }
场景2:元空间溢出(Metaspace)
典型特征
- 应用频繁使用反射、动态代理(如Spring AOP)
- JVM参数中
-XX:MaxMetaspaceSize
设置过小
排查步骤
-
查看元空间使用情况
jstat -gc <PID> | awk '{print $NF}' # 输出MC/MU列(元空间容量/使用量)
-
分析类加载器
# 使用arthas检查类加载器 classloader -t
-
解决方案
- 增大元空间:
-XX:MaxMetaspaceSize=512m
- 修复类加载器泄漏(如重复加载的OSGi模块)
- 增大元空间:
四、生产环境注意事项
- 谨慎使用jmap:Full GC可能引发STW停顿,建议在低峰期操作。
- 容器化环境:在Kubernetes中需调整Pod内存限制和JVM参数匹配。
- 监控告警:集成Prometheus + Grafana监控堆内存、GC次数等指标。
五、总结
排查OOM问题的核心思路:
- 定位内存区域 → 2. 获取内存快照 → 3. 分析对象引用 → 4. 修复代码/配置
通过工具链组合(MAT + Arthas + NMT)和监控预防,可显著降低OOM发生概率。