连载中 15/20

CPU 飙高排查:从 top 到火焰图的完整路径

2026-09-18 · 4 阅读 · 0 评论 · 0 赞

先分清是哪种 CPU 高

告警说"CPU 100%",先看是用户 CPU还是系统 CPU(top 里 us 和 sy 列)。us 高多半是业务线程在烧,sy 飙高往往跟系统调用、锁、上下文切换有关。top 整体高但 Java 进程不高,问题在别的进程;top 看 Java 进程高,再进 top -Hp PID 看具体哪个线程。

四步定位法

# 1. 找占 CPU 最高的 Java 线程
$ top -Hp $PID
#   PID  USER  %CPU  ...
#   7890 app   96.3  ...   ← 这位

# 2. 线程 ID 转十六进制
$ printf "%x
" 7890
#   1ed2

# 3. jstack 抓栈,grep 这个 nid
$ jstack $PID | grep -A 30 "nid=0x1ed2"
#   "pool-3-thread-7" prio=5 tid=0x... nid=0x1ed2 runnable
#       at java.util.regex.Pattern$Curly.match0(Pattern.java:4218)
#       at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660)
#       ... 看:CPU 烧在正则匹配上

# 4. 怀疑 GC 在烧 CPU?看 GC 日志 + jstat
$ jstat -gcutil $PID 1000 3
#   YGC YGCT  FGC FGCT  —— FGCT/YGCT 暴涨说明 GC 在生消耗

四种根因与对策

死循环/无出口递归:栈帧一直压在同一个方法,jstack 多次采样栈不变即锁定。对策是改代码。曾见过 while(flag) 里的 flag 跨线程可见性没保证,一直看不到 flag 被设——加 volatile 解决。GC 抢核:jstack 线程看着像"GC thread",jstat 看 YGCT/FGCT 在涨。根因是内存压力——频繁回收或回收效率低,得回头查内存问题不是调 GC 参数能解决的。锁自旋/活锁:线程状态是 RUNNABLE 但实际在 synchronized 或 Lock 上空转——jstack 里大量线程卡在同一 monitor。曾经一次线上 CPU 高,结果是 AQS 队列上的线程在 spin。对策是改并发策略。正则灾难:上面案例那种,恶意的正则(如 (a+)+)输入特定字符串会指数级回溯,一个匹配烧干一个核。对策是预编译 + 长度限制 + 避免灾难性正则。

火焰图:把"谁烧得多"画出来

多次采样 jstack 拼接成火焰图,方法占的横向越宽说明烧 CPU 越多。Linux 下用 perf,Java 用 async-profiler 更合适(它走 JVM 的 AsyncGetCallTrace,不靠 jstack 那种"快照"采样):./profiler.sh -d 30 -f flame.html PID 采样 30 秒,生成可交互的 SVG 火焰图,比一堆栈帧直观十倍。

预防:把排查前置成监控

CPU 飙高永远比内存问题紧急——告警响了就在事故里了。把自动采样脚本前置:CPU 超过阈值 80% 持续 1 分钟,自动跑一次 top -H + jstack + jstat 把现场存档——这样告警一来人还没上线,现场已经拍好了。这种"现场自动取证"思路同样适用于内存——下一篇讲内存泄漏排查完整版。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#CPU排查#top#火焰图#async-profiler#正则灾难

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞