--- title: "02-内存溢出" created: 2025-12-02 tags: - Java --- # 内存溢出 ## 什么是内存溢出(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 中的堆外内存分配过多,超过设置上限 | ### 示例: ```java public static void main(String[] args) { List 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 的 `dashboard`、`heapdump` 命令适合不停机在线排查。 ## 补充:相关笔记 - OOM 的"慢性前奏"(泄漏怎么逐步把内存吃光)见上一篇 [[01-内存泄漏|内存泄漏]]; - 堆、栈、元空间这些区域本身的划分见 [[01-JVM与内存模型|JVM 与内存模型]]; - OOM 时 GC 在干什么、为什么救不回来,见 [[03-垃圾回收与GC算法|垃圾回收与GC算法]]。 --- ⬅️ [[01-内存泄漏|内存泄漏]] 🏠 [[00-Java|00-Java]] ➡️ [[00-OOP特性|OOP 特性]]