jvm之常见线上问题排查

一、OOM 排查 完整流程

1. 前置配置(线上必开)

1
2
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/xxx/heap.hprof

OOM 自动生成堆快照,避免现场丢失。

2. 排查步骤

  1. 拿到进程 PID
  2. 若未自动 dump,手动导出:
1
jmap -dump:format=b,file=heap.hprof PID
  1. 使用 MAT / VisualVM 打开堆文件
  2. 核心分析点:
  • 大对象、超大集合
  • 实例数量异常多的类
  • 支配树:查看谁持有引用无法释放
  • 线程栈:定位业务代码位置

3. 常见 OOM 类型

  • Java heap space:堆内存不足、内存泄漏
  • Metaspace:元空间溢出(动态类、CGLIB、反射)
  • Direct Buffer:堆外内存溢出(NIO 未释放)

二、内存泄漏 8 大经典场景

  1. 静态集合类

    static List/Map 全局常驻,对象放入后永不回收。

  2. 单例模式

    单例长期持有业务对象引用。

  3. 资源连接未关闭

    Connection、Statement、ResultSet、IO 流、Socket 只创建不 close。

  4. 长生命周期持有短生命周期对象

    比如缓存、全局上下文持有临时对象。

  5. 内部类 / 匿名内部类

    隐式持有外部类引用,导致外部类无法回收。

  6. 缓存无淘汰策略

    LocalCache、Guava Cache 不设过期、不限制容量。

  7. ThreadLocal 未 remove

    线程复用(线程池)导致弱引用 key 回收,value 强引用泄漏。

  8. 非手动堆外内存

    ByteBuffer 直接内存、Netty 缓冲区未释放。

核心本质:无用对象被强引用持续持有,GC 无法回收


三、死锁排查 流程 + 原理

1. 死锁必要四大条件

  • 互斥条件
  • 请求与保持
  • 不可剥夺
  • 循环等待

2. 排查步骤

  1. 执行
1
jstack PID
  1. 搜索关键字:Found one Java-level deadlock
  2. 直接输出:
  • 互相等待的两个线程
  • 各自持有的锁、等待的锁
  • 精确到类名 + 行号

3. 解决

  • 统一锁顺序
  • 尝试限时锁 tryLock(time)
  • 缩小锁粒度

四、CPU 过高 排查完整步骤(面试高频)

1. 定位高 CPU 线程

  1. 查看进程 CPU
1
top
  1. 查看该进程下所有线程 CPU 占用
1
top -H -p PID
  1. 记录 CPU 最高的线程 ID,转为 16 进制
1
printf "%x\n 线程ID"

2. 定位代码行

1
jstack PID | grep -A 20 十六进制线程ID
  • 直接打印出耗 CPU 方法、堆栈、代码行号

3. 高频导致 CPU 飙高的代码

  • 死循环 while (true) 无休眠
  • 频繁正则、递归过深
  • 大量序列化 / 反序列化
  • 无限自旋 CAS、空轮询
  • 频繁 Full GC(GC 线程占满 CPU)

五、快速总结

  1. OOM

    开启 OOM 自动 dump → jmap 导出 → MAT 分析大对象 / 引用链 → 定位泄漏代码。

  2. 内存泄漏

    静态集合、单例、连接未关闭、ThreadLocal、缓存不淘汰、内部类、堆外内存、长生命周期引用。

  3. 死锁

    jstack 检索 deadlock,查看互相等待锁 + 代码行;解决:统一锁顺序、限时锁。

  4. CPU 过高

    top 定位进程 → top -H 找高 CPU 线程 → 转 16 进制 → jstack 定位具体卡死 / 循环代码。


jvm之常见线上问题排查
http://hanqichuan.com/2026/04/17/jvm/JVM之常见线上问题排查/
作者
韩启川
发布于
2026年4月17日
许可协议