⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 程序员小富 「程序员内点事」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

面试官:OOM类型有哪些?

你:就是老年代放不下了嘛!

面试官:等消息吧!

OOM(Out Of Memory) 错误有多种类型,每种类型对应不同的内存区域或触发场景。以下是常见的 OOM 类型及其产生原因:

1. java.lang.OutOfMemoryError: Java heap space

  • 触发原因:堆内存(存放对象实例)不足,无法分配新对象。

  • 典型场景:

    • 内存泄漏:对象被无意长期引用(如静态集合、未关闭的资源),无法被 GC 回收。
    • 堆大小不足:JVM 堆参数(-Xmx)设置过小,或程序需要处理的数据量超出预期。
    • 大对象分配:一次性申请超大对象(如大数组)。

示例:

// 不断向集合中添加对象导致堆溢出
List<Object> list = new ArrayList<>();
while (true) {
list.add(new Object());
}

解决方案:

  • 检查内存泄漏(使用 jmap + MAT 分析堆转储)。
  • 调整堆大小(-Xmx 和 -Xms)。
  • 优化代码逻辑,减少对象生命周期。

2. java.lang.OutOfMemoryError: Metaspace(Java 8+)或 PermGen space(Java 7-)

  • 触发原因:元空间(Metaspace)或永久代(PermGen)内存不足,用于存储类元数据、方法信息等。

  • 典型场景:

    • 动态生成大量类(如使用 CGLib、反射、动态代理)。
    • 类加载器未正确释放(如频繁部署的 Web 应用导致旧类未卸载)。

示例:

// 使用 CGLib 动态生成大量代理类
Enhancer enhancer = new Enhancer();
while (true) {
enhancer.setSuperclass(OOM.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> proxy.invokeSuper(obj, args)));
enhancer.create();
}

解决方案:

  • 调整元空间大小(-XX:MaxMetaspaceSize)。
  • 检查类加载器泄漏或动态类生成逻辑。

3. java.lang.OutOfMemoryError: Direct buffer memory

  • 触发原因:直接内存(Direct Memory,通过 ByteBuffer.allocateDirect() 分配)耗尽。

  • 典型场景:

    • 频繁申请直接内存但未及时释放(需依赖 System.gc() 或 Cleaner 机制)。
    • JVM 直接内存参数(-XX:MaxDirectMemorySize)设置过小。

示例:

// 不断申请直接内存
List<ByteBuffer> buffers = new ArrayList<>();
while (true) {
buffers.add(ByteBuffer.allocateDirect(1024 * 1024)); // 1MB
}

解决方案:

  • 检查直接内存使用代码,确保及时释放。
  • 调整 -XX:MaxDirectMemorySize。

4. java.lang.OutOfMemoryError: Unable to create new native thread

  • 触发原因:操作系统限制线程数量,无法创建新线程。

  • 典型场景:

    • 线程数超过系统限制(如 Linux 的 ulimit -u)。
    • 每个线程的栈内存(-Xss)设置过大,导致总内存占用超出。

示例:

// 无限创建线程
while (true) {
new Thread(() -> {
try { Thread.sleep(1000000); } catch (InterruptedException e) {}
}).start();
}

解决方案:

  • 减少线程数(使用线程池)。
  • 调整 -Xss 减小线程栈大小。
  • 修改系统线程数限制。

5. java.lang.OutOfMemoryError: Requested array size exceeds VM limit

  • 触发原因:尝试分配超过 JVM 限制的数组(通常接近 Integer.MAX_VALUE)。
  • 典型场景:错误计算数组长度,如 new int[Integer.MAX_VALUE]。

解决方案:

  • 检查数组长度计算逻辑,使用合理的数据结构。

6. java.lang.OutOfMemoryError: GC Overhead limit exceeded

  • 触发原因:GC 频繁执行但回收效率极低(如 98% 时间用于 GC,仅回收 2% 内存)。
  • 典型场景:堆内存几乎被占满,且存在大量无法回收的对象(内存泄漏)。

解决方案:

  • 检查内存泄漏或优化 GC 策略(如调整堆大小、更换垃圾回收器)。

7. java.lang.OutOfMemoryError: CodeCache(JIT 编译代码缓存溢出)

  • 触发原因:JIT 编译器生成的本地代码占满代码缓存区。
  • 典型场景:高频动态编译大量方法(如复杂的热点代码)。

解决方案:

  • 调整代码缓存大小(-XX:ReservedCodeCacheSize)。
  • 关闭分层编译(-XX:-TieredCompilation)。

总结

OOM 的根本原因是 JVM 内存区域不足 或 资源耗尽,需结合错误类型分析具体内存区域(堆、元空间、直接内存等)。

排查时可通过以下步骤:

1、确定 OOM 类型(通过错误日志)。

2、使用工具分析(如 jstat、jmap、VisualVM、MAT)。

3、调整 JVM 参数或优化代码逻辑。

文章目录
  1. 1. 1. java.lang.OutOfMemoryError: Java heap space
  2. 2. 2. java.lang.OutOfMemoryError: Metaspace(Java 8+)或 PermGen space(Java 7-)
  3. 3. 3. java.lang.OutOfMemoryError: Direct buffer memory
  4. 4. 4. java.lang.OutOfMemoryError: Unable to create new native thread
  5. 5. 5. java.lang.OutOfMemoryError: Requested array size exceeds VM limit
  6. 6. 6. java.lang.OutOfMemoryError: GC Overhead limit exceeded
  7. 7. 7. java.lang.OutOfMemoryError: CodeCache(JIT 编译代码缓存溢出)
  8. 8. 总结