JVM OOM 问题如何排查和解决
JVM OOM(OutOfMemoryError)是指 JVM 内存不足,无法分配新对象,导致应用崩溃,常见类型有Java heap space(堆内存不足)、Metaspace(元空间不足)、StackOverflowError(栈溢出)等,排查和解决流程如下:
# 1. 第一步:定位 OOM 类型和场景
通过异常日志和工具确定 OOM 类型:
- Java heap space:堆内存不足(最常见),多因对象创建过多且无法回收;
- Metaspace:元空间不足(JDK 8+),多因类加载过多;
- StackOverflowError:栈溢出,多因方法递归调用过深或栈内存过小;
- Direct buffer memory:直接内存不足,多因 NIO DirectBuffer 分配过多;
- Unable to create new native thread:无法创建新线程,多因线程数过多。
# 2. 第二步:获取关键数据(堆转储文件、日志)
- 生成堆转储文件:启动时添加
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/heapdump.hprof,OOM 时自动生成堆转储文件; - 查看 GC 日志:开启 GC 日志(
-Xlog:gc*:file=gc.log),分析内存分配和回收情况; - 使用工具分析:用 MAT(Memory Analyzer Tool)、JVisualVM 分析堆转储文件,定位占用内存最多的对象和引用链。
# 3. 第三步:针对性解决不同类型 OOM
- 类型 1:Java heap space(堆内存不足)
- 临时解决:增大堆内存(
-Xms4g -Xmx4g); - 根本解决:排查内存泄漏(如静态集合未清理、线程池未关闭),减少大对象创建,优化对象生命周期(如使用弱引用缓存)。
- 临时解决:增大堆内存(
- 类型 2:Metaspace(元空间不足)
- 临时解决:增大元空间(
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m); - 根本解决:减少动态类生成(如反射、动态代理),清理无用类加载器,避免过度使用框架(如 Spring 动态生成过多 Bean)。
- 临时解决:增大元空间(
- 类型 3:StackOverflowError(栈溢出)
- 临时解决:增大栈内存(
-XX:ThreadStackSize=2m); - 根本解决:修复递归调用(如添加终止条件),减少方法调用链深度(如拆分复杂方法)。
- 临时解决:增大栈内存(
- 类型 4:Direct buffer memory(直接内存不足)
- 临时解决:增大直接内存(
-XX:MaxDirectMemorySize=1g); - 根本解决:减少 DirectBuffer 分配,及时释放(如
buffer.clear()),避免长期持有直接内存引用。
- 临时解决:增大直接内存(
- 类型 5:Unable to create new native thread(无法创建线程)
- 根本解决:减少线程数(如使用线程池,核心线程数设为合理值),优化线程生命周期(如线程执行完后及时关闭)。
# 4. 第四步:长期预防措施
- 规范 JVM 参数:根据应用类型和服务器配置,合理设置堆内存、元空间、栈内存大小;
- 代码审查:避免内存泄漏(如静态集合、监听器、线程池),减少大对象和递归调用;
- 监控告警:监控堆内存使用率、元空间使用率、线程数,超过阈值及时告警;
- 定期压测:通过压测模拟高负载场景,提前发现 OOM 风险。
#
上次更新: 12/30/2025