910 字
5 分钟
JVM 基础排障:OOM、频繁 GC 与 CPU 飙高

基础排障的目标是把现象对应到证据:CPU 看热线程与栈,内存看错误类型、GC 和对象引用,不能一遇到问题就加堆或加线程。

OOM 与 StackOverflowError#

现象常见方向
Java heap space堆对象过多、大批量加载、无界缓存或队列
Metaspace大量类加载、动态生成类、类加载器无法释放
Direct buffer memory堆外直接内存占用与释放问题
unable to create native thread线程过多、系统限制或本地内存不足
StackOverflowError递归太深或调用链过深,属于栈空间问题

OOM 不只来自 Java 堆。容器被内存限制触发 OOM kill 时,也可能没有 Java 层 OOM 异常和堆转储,需要查容器或系统事件。

内存泄漏是“不再需要的对象仍被引用而无法释放”;内存溢出是无法满足分配需求。泄漏可能导致溢出,但一次性加载巨大数据也可能溢出,未必泄漏。

内存与频繁 GC 怎么查?#

  1. 确认错误和时间范围,查看发布、流量、批量任务等变化。
  2. 结合 GC 日志看回收频率、停顿、回收前后占用,判断分配过快还是存活对象持续累积。
  3. 查看对象直方图,必要时获取堆转储,分析大对象、集合和到 GC Roots 的引用路径。
  4. 根据证据限制批次、队列和缓存,修复残留引用,再评估容量配置。

堆转储中对象多只能提示方向;判断泄漏需要结合对象用途、引用路径和占用随时间的变化。Full GC 后仍持续增长的占用值得关注,但不能仅凭一次日志断言泄漏。

CPU 高怎么查?#

在 Linux 上,先定位进程,再通过 top -H -p <pid> 等查看热线程;将线程 ID 转为十六进制,与线程转储中的 nid 对照,查看它正在执行什么。多次采样区分长期热点和瞬时波动。

重点查死循环、过度重试、热点计算、序列化、频繁 GC 等。锁等待通常直接体现为等待与延迟,不应看到阻塞就断言它一定消耗大量 CPU。

常用工具与参数#

Terminal window
jcmd <pid> Thread.print
jcmd <pid> GC.heap_info
jcmd <pid> GC.class_histogram
jcmd <pid> GC.heap_dump /path/app.hprof

命令是否可用取决于 JDK、权限和运行环境。直方图、堆转储等可能带来停顿、额外 GC 和大文件写入,应评估影响后在合适窗口操作。

  • -Xms:初始堆大小;-Xmx:最大堆大小。
  • -Xss:线程栈大小;线程越多,总栈内存也越大。
  • -XX:MaxMetaspaceSize:元空间上限,不是堆上限。
  • -XX:+HeapDumpOnOutOfMemoryError:在支持的 OOM 场景生成堆转储,配合路径和磁盘空间规划。
  • JDK 9+ 可以通过 -Xlog:gc* 等统一日志选项观察 GC;旧版本参数不同。

面试回答模板#

先确认现象和运行环境,OOM 要按错误类型区分堆、元空间、直接内存和线程资源;频繁 GC 要看回收前后占用与分配速度,必要时分析堆和引用路径;CPU 高要定位热线程并对照线程栈,多次采样确认热点。修复应根据证据选择限流、容量约束或代码改动,而不是直接扩大所有资源。

自测#

  1. 堆未满就不会 OOM 吗?不是,可能耗尽其他内存或线程资源。
  2. 频繁 Full GC 一定是泄漏吗?不一定。
  3. 堆转储命令是完全无开销的吗?不是,要评估停顿与磁盘成本。
JVM 基础排障:OOM、频繁 GC 与 CPU 飙高
https://blog.xqcherry.top/posts/interview/jvm/jvm-basic-troubleshooting/
作者
xqcherry
发布于
2026-10-12
许可协议
CC BY-NC-SA 4.0