内存溢出

什么是内存溢出(OutOfMemoryError,OOM)

概念:

OOM 是指程序运行时 内存空间耗尽,JVM 无法再分配对象或资源时抛出的错误,属于严重的错误类型(Error),程序无法继续运行。

常见的 OOM 类型:

错误类型 原因举例
Java heap space 对象过多,堆内存不够,如无限循环创建对象
GC overhead limit exceeded GC 花太多时间但几乎回收不到内存,系统已“垂死挣扎”
PermGen space(Java 8 前) 类加载太多,永久代爆了(常见于动态生成类、反复部署 web 项目)
Metaspace(Java 8+) 类元信息过多导致元空间(Metaspace)耗尽
unable to create new native thread 系统线程数量超过限制,创建新线程失败
Direct buffer memory NIO 中的堆外内存分配过多,超过设置上限

示例:

public static void main(String[] args) {
    List<byte[]> list = new ArrayList<>();
    while (true) {
        list.add(new byte[10 * 1024 * 1024]); // 不断占用堆
    }
}
// 运行一会儿会报:java.lang.OutOfMemoryError: Java heap space

解决策略:

  • 优化代码逻辑,避免大量无效对象创建
  • 限制集合增长、线程数量、反射类生成
  • 合理配置 JVM 参数(如 -Xmx, -XX:MaxMetaspaceSize 等)
  • 使用监控工具(VisualVM、MAT、Arthas)定位 OOM 根因

补充:一张复现实验清单

OOM 类型不同,复现姿势也不同。都带上 -Xmx/-Xss 限制跑,几秒内就能看到抛错:

想复现什么 最小实验
Java heap space while(true) list.add(new byte[1024*1024])(即上文示例)
Metaspace CGLIB/ASM 在循环里动态生成类:Enhancer e = new Enhancer(); e.setSuperclass(Clazz.class); e.create();
unable to create new native thread 循环 new Thread(() -> LockSupport.park()).start()
Direct buffer memory 循环 ByteBuffer.allocateDirect(1024*1024)

亲手各跑一遍,比背八股记得牢得多——每个 Error 抛出的瞬间,正好对应 JVM 的一块内存区域(堆/元空间/线程栈/堆外)。

补充:线上 OOM 排查的标准动作

  1. 先留住现场:JVM 参数加 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/dump/,OOM 发生时自动落一份 hprof;
  2. 看 Error 全文Java heap space 还是 Metaspace 还是 native thread,先定位是哪块区域的问题;
  3. MAT 分析 hprof:Leak Suspects + Dominator Tree 找到占内存最大的对象和引用链;
  4. 对照业务:大对象是缓存没上限?一次查询全表?还是批量任务并发太高?

Arthas 的 dashboardheapdump 命令适合不停机在线排查。

补充:相关笔记


⬅️ 内存泄漏 🏠 00-Java ➡️ OOP 特性