P03 线上突发 CPU 飙高怎么定位和解决
面试题:线上接口突然卡死,服务器 CPU 直接飙到 100%+,如何快速定位到是哪一行代码导致的?
核心答案:三命令定位法
第 1 步:top 找到"罪魁祸首"进程
执行 top,按 CPU 占用排序,找到 CPU 消耗最高的 Java 进程的 PID。
示例:进程 18720 的 CPU 占用达到 195%(说明它占用了接近两个核),基本可以确定就是它在搞事情。
第 2 步:top -H 找到进程内最耗 CPU 的线程
bash
top -H -p <PID>-H 表示按线程(Thread)维度展示,-p 指定进程。此时能看到该进程内 CPU 占用最高的几个线程 ID(十进制)。
然后把这个线程 ID 转成 16 进制:
bash
printf "%x\n" <线程ID>因为 jstack 输出里的线程号 nid 是十六进制的,需要对齐才能匹配。
第 3 步:jstack 过滤该线程的调用栈,定位到具体代码
bash
jstack <进程PID> | grep <16进制线程ID> -A 20-A 20 表示显示匹配行及其后 20 行。输出里的调用栈顶部就是该线程正在执行的代码,一路往下就能定位到具体类、方法、行号。
示例:最终定位到 UserController.getUser() 方法的第 19 行,打开代码一看,原来这里写了一个死循环——CPU 飙高的根源。
加分点
top默认展示进程;top -H展示线程(线程号即 LWP/轻量级进程号)。- 线程 ID 转十六进制也可以用
printf "%x\n" <tid>,这是面试官常追问的细节。 - 如果 CPU 飙到 N × 100%,说明有 N 个线程在忙,往往不是单点问题。
- CPU 飙高的常见原因不止死循环:频繁 Full GC、正则回溯灾难、JSON 大对象序列化、自旋等待等。若怀疑 GC,可再用
jstat -gcutil <pid> 1000观察 GC 频率。 - 配合
jstack多打几次,如果某线程一直卡在同一位置,基本就是问题线程。
一句话总结
top 找进程 → top -H -p <pid> 找线程 → 线程号转十六进制 → jstack <pid> | grep -A 20 <十六进制tid> 定位到具体代码行。