java应用cpu飙升时,应先用top -hp查高cpu线程的十进制id,转为小写十六进制后在jstack快照中搜索nid匹配,定位到runnable状态下的源码行(如userservice.java:42),再通过多次jstack采样确认是否持续停留该行以判断死循环。

当 Java 应用 CPU 使用率突然飙升,大概率是某个线程陷入了高耗时操作,最常见的是死循环或密集计算。单纯看 top 只能看到进程级 CPU 占用,必须下钻到线程级,并关联到具体 Java 方法和源码行号,才能快速根治问题。核心思路是:用 top -Hp 找出高 CPU 线程的十六进制 ID,再用 jstack 抓取线程快照,将线程 ID 转换后匹配堆栈,最终定位到代码行。
第一步:用 top -Hp 找出最“烫”的线程 PID
在问题发生时(不要等告警才查),立刻执行:
top -Hp <java_pid></java_pid>
按 P(大写)按 CPU 使用率倒序,顶部几行就是当前最耗 CPU 的线程。记下第一列的 **十进制线程 ID(LWP)**,比如 12345。
注意:top -Hp 显示的是操作系统线程 ID(即 LWP),它和 jstack 中显示的线程 ID 格式不同——jstack 里用的是十六进制,且不带 0x 前缀。所以需要转换。
第二步:将线程 ID 转为十六进制,用于 jstack 匹配
把上一步拿到的十进制 ID(如 12345)转成小写十六进制(不带前缀):
printf "%x\n" 12345 → 输出 3039
这个 3039 就是你要在 jstack 输出中搜索的关键字。它对应线程堆栈里的 tid=0x3039 或 nid=0x3039(不同 JDK 版本写法略有差异,但 nid 最常见)。
第三步:用 jstack 抓快照并精准定位源码行
执行:
jstack <java_pid> > jstack.out</java_pid>
打开 jstack.out,搜索 3039(刚才算出的十六进制值)。你会看到类似这样的段落:
"http-nio-8080-exec-25" #75 daemon prio=5 os_prio=0 tid=0x00007f8b4c0a1000 nid=0x3039 runnable [0x00007f8b3a7d6000] java.lang.Thread.State: RUNNABLEat com.example.service.UserService.findUserById(UserService.java:42)at com.example.controller.UserController.get(UserController.java:33)
关键看两处:
- State 是 RUNNABLE(不是 BLOCKED 或 WAITING),说明它正在 CPU 上跑,不是锁等待;
-
堆栈最顶端的方法调用(如
UserService.java:42)就是当前执行点——死循环极大概率就在这里或其调用链内(比如第 42 行是个 while(true) 或 for 循环,且循环体内没 sleep、没 I/O、没退出条件)。
第四步:交叉验证与快速确认
光看一行不够稳?再补两招增强判断:
- 对同一个线程多抓几次
jstack(间隔 1~2 秒),如果每次堆栈顶端都停在 同一行 Java 代码,基本可断定是死循环或极长计算; - 检查该行附近是否有明显风险点:无界 while、for(i=0; i
- 若应用启用了调试符号(编译时加
-g)且未 strip,jstack 就能打出精确行号;否则可能只到方法名,需结合代码逻辑反推。 - 若应用启用了调试符号(编译时加
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











