CPU 高、应用卡住和长暂停是三类不同症状。排查顺序是先冻结时间范围和系统指标,再采集多份线程证据,最后用 JFR、GC 日志或 Profile 验证原因。重启会清除关键现场,应先完成低影响采集。
Table of contents
Open Table of contents
CPU 飙高:把系统线程对应到 Java 栈
启动 60 秒的CPU 热点样例下载:
java -cp target/classes dev.elaine.jvmlab.CpuHotspotSample 60
在另一个终端连续采集三份线程转储和一份 JFR:
jcmd <PID> Thread.print -l > thread-1.txt
jcmd <PID> JFR.start name=incident settings=profile duration=30s filename=incident.jfr
jcmd <PID> Thread.print -l > thread-2.txt
jcmd <PID> Thread.print -l > thread-3.txt
持续处于 RUNNABLE 且多份栈都停在 isPrime 的线程,才是可信热点。单份线程栈只是一张照片,可能恰好拍到正常短任务。
死锁:验证锁等待环
启动死锁样例下载:
java -cp target/classes dev.elaine.jvmlab.DeadlockSample
jcmd <PID> Thread.print -l > deadlock.txt
Thread.print 会报告 Java-level deadlock,并列出线程持有和等待的监视器。实验进程不会主动结束,采集后运行
kill <PID>;不要使用模糊的进程名批量终止。
修复策略是统一锁顺序、缩小临界区,或改用带超时的并发结构。增加测试时应使用超时断言,避免 CI 永久挂起。
长暂停:先确定谁暂停了进程
请求延迟突然上升不一定是 GC。可能原因包括:
- GC 暂停或 Safepoint 操作。
- CPU 被其他进程抢占或容器节流。
- 磁盘、DNS、数据库或网络阻塞。
- 锁竞争、线程池耗尽或任务队列堆积。
- 宿主机冻结、虚拟化迁移或时间源异常。
同时对齐应用延迟、-Xlog:gc*,safepoint、容器 CPU throttling、系统负载和下游指标。若延迟峰值没有对应 GC/Safepoint,
继续找其他层,不要先改 GC 参数。
线上应急最小清单
- 记录开始时间、实例、版本、流量和告警指标。
- 保存系统 CPU、内存、线程数和容器限制。
- 间隔采集至少三份线程转储。
- 导出持续 JFR 或启动短时录制。
- 保存 GC 与 safepoint 日志。
- 评估磁盘和停顿风险后再生成 Heap Dump。
- 完成证据采集后执行限流、摘流或重启。
命令和处置人必须进入事件时间线,便于事后区分原始故障与诊断动作造成的影响。
下一步
阅读常量池、字符串与类元数据的内存代价,理解非业务对象如何 扩大长期存活集。