Skip to content
极高进阶

一句话答案

OOM 按区域分:堆溢出(对象过多/泄漏)、栈溢出(递归过深)、元空间溢出(类过多)、直接内存溢出(NIO ByteBuffer)。

核心要点

各区域的 OOM 情况:

区域会不会 OOM典型错误常见原因
✅ 会java.lang.OutOfMemoryError: Java heap space内存泄漏、对象太多、堆设置太小
方法区/Metaspace✅ 会OutOfMemoryError: Metaspace动态生成大量类(CGLIB / Groovy 脚本),类卸载缓慢
虚拟机栈✅ 会(栈溢出)StackOverflowError无限递归;线程过多时 OutOfMemoryError: unable to create new native thread
本地方法栈✅ 会StackOverflowError同虚拟机栈
程序计数器❌ 不会唯一不会 OOM 的区域

线上排查 OOM 步骤:

第一步:获取 Heap Dump

bash
# 方式1:JVM 启动参数配置(推荐,OOM 时自动 dump)
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/dump/heapdump.hprof

# 方式2:手动 dump(进程仍在运行时)
jmap -dump:live,format=b,file=heapdump.hprof <pid>

第二步:分析 Heap Dump

  • 工具:MAT(Memory Analyzer Tool)VisualVM
  • 查找:Retained Heap(保留堆大小)最大的对象
  • 定位:Dominator Tree(支配树)找出谁持有大量内存

第三步:结合代码定位根因

  • 常见原因1:内存泄漏(如静态 Map 不断 put 却不 remove,或未关闭的连接/流)
  • 常见原因2:缓存没有大小上限(如本地缓存无 eviction 策略)
  • 常见原因3:大批量数据一次性加载(分页查询改成 SELECT *
  • 常见原因4:Metaspace OOM:检查是否有大量动态类生成(用 -XX:+TraceClassLoading

辅助工具:

bash
jstat -gcutil <pid> 1000   # 每秒输出 GC 统计,观察堆区使用率
jinfo -flag MaxMetaspaceSize <pid>  # 查看 Metaspace 上限
Arthas: memory 命令实时查看各区域内存使用
追问与易错

追问方向:

  • Java heap space 和 GC overhead limit exceeded 区别?(后者是 GC 时间过长)
  • 元空间 OOM 一般什么原因?(动态代理/CGLIB 生成大量类)
  • 线上出了 OOM 你怎么排查的?(dump→MAT→Dominator Tree→泄漏路径)

易错点:

  • ❌ "OOM 就是堆溢出"——还有栈溢出/元空间/直接内存 OOM
  • ❌ 不配置 HeapDumpOnOOM——OOM 只发生一次,错过现场很难复现

💡 记忆锚点

OOM四大灾区:堆溢出(对象太多/泄漏)、栈溢出(递归无底洞)、Metaspace溢出(动态类爆炸)、直接内存溢出(NIO)。排查三步走:HeapDump抓现场、MAT查支配树、顺藤摸瓜找代码。救命参数:-XX:+HeapDumpOnOutOfMemoryError,不配就是丢掉黑匣子。