Java 服务 CPU 打满排查笔记

Java 服务 CPU 打满排查笔记

记录时间:2026-09-19
环境:Linux 服务器 / Java 服务

一、问题现象

线上 Java 服务 CPU 飙到 400%,接口卡死。我没急着重启,先保住现场,再顺着”进程 → 线程 → 堆栈 → 代码行”往下查。

二、排查过程

2.1 找进程 PID

top

top 里按 CPU 从高到低排,最顶上那个 Java 进程就是目标,这次拿到的是 11119

2.2 找高 CPU 线程

top -Hp 11119

找到 CPU 最高的线程,记下它的 ID,这次是 11146

2.3 线程 ID 转十六进制

printf "%x\n" 11146

输出 2b8a,就是 11146 转成十六进制的值。

2.4 jstack 抓堆栈定位代码行

jstack 11119 | grep -A 5 -i "0x2b8a"

输出示例:

"pool-1-thread-1" #16 prio=5 os_prio=0 cpu=9945233.19ms elapsed=11592.52s tid=0x00007f4128005ba0 nid=0x2b8a runnable [0x00007f41d0942000]
   java.lang.Thread.State: RUNNABLE
        at OrderService.lambda$main$0(OrderService.java:11)

堆栈直接把 OrderService.java:11 指出来了,卡死 CPU 的就是这一行。

2.5 进阶用法

单次 grep 能定位,但这几种场景我会换命令:

场景 命令 作用
保留完整现场 jstack -l 11119 > jstack_dump.txt 全部线程存文件慢慢看,-l 多打锁信息,查死锁要用
判断持续还是偶发 隔 3 秒连抓 3 次 jstack 对比三次栈顶,区分持续死循环和偶发尖刺
查死锁无响应 jstack -l 11119 \| grep BLOCKED 直接过滤阻塞线程,找互相等待的链条

三、根因分析

我看 jstack 输出,心里先有数了:线程状态是 RUNNABLE,CPU 时间片累计 995 秒,说明它一直在跑、没阻塞过。问题落在 OrderService.java:11,是个 Lambda 里的逻辑。持续吃 CPU 又不阻塞,基本就两种:死循环,或者单次计算量太大。翻一眼第 11 行,是哪种一看就明白。

四、验证

我核对了一遍:堆栈里的 nid=0x2b8aprintf 转出来的十六进制对得上,OrderService.java:11 也在最近改动的文件里,到这里基本能确定就是它了。

五、注意事项

  • 先保现场,别急着重启。一重启线程堆栈就没了,再想定位只能等下一次复现。
  • top -Hp 里的线程 ID 是十进制,jstack 里的 nid 是十六进制,两边对不上号,必须先转成十六进制才能 grep。
  • jstack 得用启动服务那个用户跑,不然报 attach 失败,权限对不上。
  • 一次 grep 没匹配到别慌,热点线程可能会换,多抓几次对比栈顶就行。
  • JDK 版本要跟起服务的一致,拿 A 版的 jstack 抓 B 版起的进程,直接报错拿不到堆栈。
  • CPU 已经打满的时候别反复抓 jstack,它本身也占 CPU。抓一两次把 dump 落盘就收手。
  • 栈里状态是 RUNNABLE,就是在占着 CPU 空跑。九成 CPU 飙高都栽在这个状态下的死循环上,优先盯它。

六、参考资料

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇