大家好,我是程序员二叉。
简介
本文整合面试 + 线上实战 全套 JVM 故障排查核心知识点:四大 OOM 类型成因、常用 JVM 参数、线上 OOM 标准排查流程、频繁 FullGC 定位方案、内存泄漏与内存溢出区别、CPU 飙高/线程死锁排查、JDK 工具命令、HeapDump 抓取与分析全流程,干货拉满。欢迎点赞关注收藏。
一、常见 4 种 OOM 类型及底层原因
1. 堆内存溢出(Java heap space)
报错: java.lang.OutOfMemoryError: Java heap space
核心原因:
- 内存泄漏:无用对象持续被引用,GC 无法回收
- 集合无限累加(List/Map 只增不减)
- 一次性加载超大批量数据、未分页查询
-Xmx最大堆内存设置过小
2. 元空间溢出(Metaspace)
报错: java.lang.OutOfMemoryError: Metaspace
核心原因:
- 频繁动态生成 Class(CGLib、动态代理、SpringAOP)
- 项目热部署、热加载频繁重复加载类
- 大量反射、动态类加载场景
- 配置了过小的
MaxMetaspaceSize
3. 栈溢出(StackOverflowError)
报错: java.lang.StackOverflowError
核心原因:
- 方法无限递归调用
- 递归深度过大
- 线程栈
-Xss设置过小
注意:栈溢出不属于 OOM,是线程栈帧溢出异常
4. 直接内存溢出(Direct buffer memory)
报错: java.lang.OutOfMemoryError: Direct buffer memory
核心原因:
- NIO 使用
allocateDirect()频繁分配直接内存 - 直接内存未手动释放
- 不受 -Xmx 限制,耗尽系统物理内存导致溢出
二、高频 JVM 参数详解(面试必背)
| 参数 | 作用说明 |
|---|---|
| -Xms | 初始堆内存大小,建议与 Xmx 一致 |
| -Xmx | 最大堆内存大小 |
| -Xss | 单个线程栈内存大小 |
| -Xmn | 新生代整体内存大小 |
| -XX:MetaspaceSize | 元空间初始值 |
| -XX:MaxMetaspaceSize | 元空间最大值,防止类加载OOM |
| -XX:MaxDirectMemorySize | 限制NIO直接内存上限 |
| -XX:+HeapDumpOnOutOfMemoryError | OOM时自动导出堆快照 |
| -XX:HeapDumpPath | 指定dump文件存放路径 |
| -XX:+PrintGCDetails | 打印详细GC日志 |
三、线上 OOM 完整排查流程(标准实战流程)
- 提前配置JVM参数:OOM自动dump快照
- 服务崩溃后获取
.hprof堆文件 - 使用 MAT / VisualVM / JProfiler 打开分析
- 查看大对象、实例数量、集合占用Top
- 追踪 GC Roots 引用链,定位无法回收对象
- 排查代码:静态集合累积、ThreadLocal未清理、IO未关闭、内存泄漏
- 代码优化:分页、手动释放、移除长引用
- 压测验证问题修复
四、频繁 Full GC 完整排查步骤
线上90%频繁FullGC = 内存泄漏
jstat -gc PID 1000实时观察GC变化- 发现老年代持续上涨、频繁FullGC
- 查看GC日志,确认是空间不足/元空间满/手动GC
- dump堆快照分析存活大对象
- 定位长期累积无法回收的集合/对象
- 优化方案:
- 修复内存泄漏
- 批量查询分页处理
- 减少大对象创建
- 合理调优堆、元空间大小
五、内存泄漏 VS 内存溢出(高频面试)
1. 内存泄漏(Memory Leak)
- 定义 :对象已无用,但存在有效引用,GC无法回收
- 本质:对象占着内存不释放
- 后果:内存缓慢上涨,最终触发OOM
典型泄漏场景:
- static 静态集合缓存无限累加
- ThreadLocal 使用不 remove
- 连接、IO 流不关闭
- 长生命周期持有短生命周期对象引用
2. 内存溢出(OOM)
- 定义:JVM 内存资源耗尽,无法分配新对象
- 本质:内存不够用
核心关系
内存泄漏是根源,内存溢出是最终结果
六、线上 CPU 飙高排查流程(生产万能套路)
top找到 CPU 100% 的 Java 进程 PIDtop -Hp PID查看进程内最耗CPU线程TIDprintf "%x TID"转为 16进制jstack PID | grep 16进制ID -A30- 精准定位业务代码行
常见原因:
- 代码死循环
- 复杂逻辑运算、大数据遍历
- 频繁GC、内存抖动
- 锁自旋、线程竞争
七、线程死锁排查方法
jps获取Java进程号jstack PID打印线程快照- 检索关键字:
deadlock、waiting to lock、locked - jstack 自动输出死锁线程 + 代码行数
- 调整锁顺序、加锁超时解决
八、JVM 五大排查命令作用总结
jps
查看所有Java进程PID,基础排查入口
jstack
线程问题神器:排查死锁、CPU飙高、线程阻塞、线程卡死
jmap
内存问题神器:查看堆使用、实例数量、导出dump文件
jstat
GC神器:实时监控新生代、老年代、GC次数、GC耗时
jhat
JDK内置dump分析工具(现已淘汰,用MAT)
九、堆 Dump 抓取 + 分析完整实战
1. 手动抓取dump
bash
jmap -dump:format=b,file=heap.hprof PID
2. 线上自动抓取(推荐配置)
bash
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/dump/
3. MAT 分析步骤
- 打开 hprof 文件
- Histogram:查看对象数量排行
- Dominator Tree:查看大对象占用排行
- 追踪 GC Root 定位泄漏源头
- 精准修复代码问题
十、全文核心总结(面试速背)
- 四类 OOM:堆溢出、元空间溢出、栈溢出、直接内存溢出
- 内存泄漏:对象无用不释放,是线上 OOM 最大元凶
- CPU 飙高排查链路:top → top -Hp → 16 进制 → jstack
- FullGC 频繁:绝大多数是内存泄漏导致
- OOM 排查核心:dump 快照 + MAT 分析 GC Roots 引用链
- 常用参数:Xms/Xmx 堆、Xss 栈、Metaspace 元空间
- 直接内存:不受堆限制,物理内存满照样 OOM