内存溢出
什么是内存溢出(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 排查的标准动作
- 先留住现场:JVM 参数加
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/dump/,OOM 发生时自动落一份 hprof; - 看 Error 全文:
Java heap space还是Metaspace还是native thread,先定位是哪块区域的问题; - MAT 分析 hprof:Leak Suspects + Dominator Tree 找到占内存最大的对象和引用链;
- 对照业务:大对象是缓存没上限?一次查询全表?还是批量任务并发太高?
Arthas 的 dashboard、heapdump 命令适合不停机在线排查。
💬 评论