定位Java应用CPU飙升需按顺序确认进程PID、线程TID、栈中业务代码行:先用top找高CPU java进程PID,再用top -Hp或ps -T查最高CPU线程TID,接着将TID转小写十六进制,最后用jstack抓栈并搜索nid=0x...定位RUNNABLE状态下的业务方法。

Java 应用 CPU 飙升时,线程栈是定位问题代码最直接的证据。关键不是“看有没有堆栈”,而是按顺序抓准三处信息:哪个进程、哪个线程、哪一行代码在持续吃 CPU。
第一步:确认高 CPU 的 Java 进程 PID
运行 top,按 P(大写)按 CPU 使用率倒序排列,找到 %CPU 明显异常的 java 进程(比如长期 >80% 或远超核数 × 100%)。记下它的 PID(如 12345)。
也可用快捷命令缩小范围:
- ps -ef | grep java | grep -v grep
- top -bn1 | grep java
第二步:找出进程内最忙的线程 TID
对上一步的 PID 执行线程级监控:
- top -Hp 12345 —— 进入后按 P 排序,找 %CPU 最高的那行,记录其 PID 列(即 TID,十进制,如 12367)
- 或用精简命令:ps -T -p 12345 -o tid,pcpu,cmd --sort=-pcpu | head -n 5
注意:Linux 中线程和进程共用 PID 字段,这里的“PID”实际是线程 ID(TID)。
立即学习“Java免费学习笔记(深入)”;
第三步:把 TID 转成十六进制,匹配 jstack 输出
JVM 线程栈里的线程 ID 是小写十六进制(格式为 nid=0xabc),需转换:
- printf "%x\n" 12367 → 得到 304f(记住不加 0x 前缀,jstack 搜索时用 0x304f 或直接搜 304f)
- 验证:若输出含字母,确保是小写(jstack 不认大写)
第四步:用 jstack 抓栈并定位活跃代码行
执行:
- jstack 12345 > stack.log(推荐先落地文件,避免终端刷屏丢失)
- 再搜索:grep -A 15 "nid=0x304f" stack.log(-A 15 展开后续 15 行,大概率覆盖完整调用链)
重点看三块:
-
线程名与状态:如
"pool-1-thread-2" #15 prio=5 ... java.lang.Thread.State: RUNNABLE—— RUNNABLE 状态才真正代表正在 CPU 上跑 -
堆栈顶方法:关注
at com.xxx.Service.process(...)这类业务代码行,尤其是反复出现、无 sleep/IO/锁等待的循环体 -
典型危险信号:
- 死循环:
while (true) { ... }或for(;;) { ... } - 正则回溯:
Pattern.compile("a+b+c+").matcher(input).find()遇到恶意输入 - 高频计算:
BigDecimal.divide(...)在循环中未设精度、未缓存 - 空忙等:
while (flag == false) { } // 缺少 Thread.sleep 或 volatile
- 死循环:
补充技巧:跳过手动 grep,一步到位
如果权限允许且环境干净,可管道直出:
- jstack 12345 | grep -A 20 "nid=0x$(printf "%x" 12367)"
- 配合 Arthas 更快:thread -n 1(查最忙线程)、thread <tids>(查指定线程栈)
不复杂但容易忽略:线程栈只能告诉你“正在执行什么”,不能直接说明“为什么慢”。所以看到 RUNNABLE 后,要结合代码逻辑判断——是算法复杂度问题?数据量突增?还是外部依赖(如序列化大量对象)引发的隐式计算?


















