定位java服务cpu 100%问题:先用top找进程pid,再用top -hp [pid]按shift+p排序定位高cpu线程tid,printf '%x'转换为小写十六进制,jstack [pid] | grep -a 10 '0x[tid]'查堆栈,聚焦runnable状态下的业务方法。

Java服务在Linux上把CPU打到100%,大概率不是硬件瓶颈,而是代码或配置出了问题。直接杀进程或重启治标不治本,关键得定位到具体线程和方法。
top -Hp 定位高CPU线程
先用 top 找出 Java 进程 PID,再用 top -Hp [pid] 切换到线程视图。注意按 Shift + P 按 CPU 使用率排序,一眼就能看到占用最高的那个 TID(线程 ID)。别只盯着进程级的 %CPU——一个 Java 进程可能有上百个线程,真正“飙车”的往往只是其中一两个。
-
top -Hp必须加-p参数指定 PID,否则会列出所有线程,干扰判断 - 如果看到多个线程 CPU 占用都接近 100%,可能是线程池无界扩容、或大量短生命周期线程被反复创建
- 留意线程状态:
R(Running)才真正在消耗 CPU;S(Sleeping)或Z(Zombie)基本可排除
printf '%x' 转十六进制线程 ID
Java 的 jstack 输出里,线程 ID 是十六进制格式(如 nid=0x7d4),而 top -Hp 显示的是十进制。必须转换才能匹配。用 printf '%x\n' [tid] 最稳,比 echo 'obase=16;...' | bc 更少出错(尤其 tid 带前导零时)。
- 转换后字母必须小写,
jstack不认大写0X7D4 - 别漏掉
0x前缀——grep时要搜0x7d4,不是7d4 - 如果
printf报错,说明输入的 tid 不是纯数字,检查是否误复制了其他字段
jstack | grep -A 10 定位热点栈帧
执行 jstack [pid] | grep -A 10 '0x[tid]',能快速捞出该线程的堆栈。重点看最上面几行——也就是它正在执行的方法。如果反复采样(间隔 2~3 秒)都停在同一个方法,基本就是根因。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 常见陷阱:
GC线程(如G1 Refine、VM Thread)也可能占 CPU,但它们出现在jstack里通常带VM或GC字样,不是业务代码 - 看到
java.lang.Thread.State: RUNNABLE且下一行是你的类名+方法,八成就是死循环、正则回溯、或密集计算 - 如果堆栈里全是
Unsafe.park或Object.wait,说明线程在等锁或条件变量,这时候要查deadlock或线程池饱和
用 show-busy-java-threads.sh 一键抓热点
线上救火时,手动敲四条命令太慢。直接用 oldratlee 的 show-busy-java-threads.sh,一条命令输出前 3 个最忙线程的堆栈和对应代码行(需已安装 JDK 并配置好 JAVA_HOME):
source <p>它内部自动完成 <code>top -Hp</code> → 转十六进制 → <code>jstack</code> → 提取栈帧 → 关联源码行号。省去手误风险,也避免在 <code>vim</code> 里翻半天找不到匹配项。</p>
- 脚本默认只分析 Java 进程,不会误伤
nginx或mysql - 如果输出为空,检查是否没权限读取目标进程(非 root 用户可能无法
jstack其他用户的 Java 进程) - 它不解决 GC 频繁或内存泄漏,那得配合
jstat -gc和jmap
真正难的不是找到那一行代码,而是判断它为什么会被高频调用——比如一个时间工具类方法被放在循环里每秒执行上千次,或者数据库连接池配置过小导致请求排队时线程空转自旋。这些上下文,得结合业务逻辑和调用链路日志来看。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










