线上接口变慢、CPU 飙高,完整的排查路径是怎样的?

结论先行:排查走“先资源、再进程、后线程、终代码/依赖”的分层路径,每层都要拿到证据再下结论。接口慢的四大方向:CPU 忙(死循环/GC/正则回溯/序列化)、锁与线程阻塞、数据库慢 SQL、下游依赖慢。CPU 飙高的标准三步:top 找进程 → top -Hp 找线程 → jstack 转十六进制找栈。

排查步骤(带命令)

  1. 全局看:top / uptime 看 load 与 CPU,先判断是 CPU 忙还是 IO/等待型慢;
  2. 进程级:top -Hp <pid> 找 CPU 最高的线程,记下十进制线程号 n;
  3. 线程级:jstack <pid> > j.log,用 printf '%x\n' n 转十六进制,在栈文件里搜索该 tid;
  4. 栈分类:RUNNABLE + 自旋/正则/加解密 → 计算热点;WAITING/BLOCKED → 锁与连接池等待;
  5. 配套取证:jstat -gcutil <pid> 排除 GC 抖动;慢接口再查数据库慢查询日志与 EXPLAIN、下游 trace 耗时。

现象快速对照表

现象第一嫌疑下一步动作
CPU 100% 且 GC 不多业务死循环/正则/序列化jstack 找 RUNNABLE 热点代码
CPU 高 + GC 频繁对象过多/泄漏jstat/jmap 观察堆与存活对象
CPU 低但接口慢锁等待/DB/下游看 BLOCKED 栈、慢 SQL、RPC trace
线程数暴涨连接池耗尽/线程泄漏多次 jstack 数线程栈去重

一次 jstack 定位示例

top -Hp 12345          # 发现线程 23456 的 CPU 最高
printf '%x\n' 23456    # 输出十六进制 5ba0
jstack 12345 > j.log && grep -A 30 '5ba0' j.log
# "http-nio-8080-exec-3" ... RUNNABLE
#   at com.demo.LoopService.run(LoopService.java:42)   ← 热点代码行

常见追问 / 记忆点

  • 追问:多次 jstack 采样能看出什么?答:同一栈反复出现 = 稳定热点;每次栈都不同且大量 BLOCKED = 锁竞争或线程池耗尽。
  • 追问:先怀疑 GC 还是先怀疑代码?答:用 jstat 先证伪/证实 GC,再决定看堆还是看线程栈,避免方向性浪费时间。
  • 记忆点:四步曲“top → top -Hp → jstack → 十六进制找栈”;先排除 GC/DB/依赖,再咬定代码热点。
笔记加载中…