news 2026/9/14 21:36:11

11-CPU 飙高与死锁排查

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
11-CPU 飙高与死锁排查

本篇是「JVM 与性能调优系列」第 11 篇。

负载没变,CPU 却跑满,接口全超时。top 看是 Java 进程,但哪个线程干的?死锁更隐蔽——不报错、不崩溃,就是所有请求静静卡住。两件事,一套排查法。


一、CPU 飙高的典型成因

先别急着查,先分类,因为不同成因走不同路径:

  • 死循环 / 频繁计算:某段代码在无出口地跑,单线程吃满一个核
  • 频繁 GC:老年代涨、Full GC 不停,多个 GC 线程抢 CPU(呼应第 9、10 篇)
  • 锁竞争激烈:大量线程抢同一把锁,上下文切换开销暴涨
  • 序列化/正则/反射滥用:隐性 CPU 消耗大户

分类清楚了,才能选对工具。下面以最常见的「死循环 + 频繁 GC」为例走一遍。


二、CPU 飙高排查六步

这是每个 Java 工程师都该背下来的套路:

  1. top:找到 CPU 最高的进程 PID
  2. top -Hp <PID>:找到该进程内 CPU 最高的线程 TID
  3. printf "%x\n" <TID>:把十进制线程 ID 转成十六进制(jstack 里线程号是十六进制)
  4. jstack <PID> | grep -A 20 <nid>:用十六进制 nid 定位线程栈
  5. 看栈里这个线程正在执行什么方法(死循环?GC?业务?)
  6. 结合源码定位根因,修复

关键点:第 4 步要快,因为线程栈是瞬时的。生产上常用脚本一次抓完,或借助 Arthas 一步到位。


三、实战:死循环导致飙高

jstack抓到的栈里,如果同一个线程反复出现、停在类似位置:

"business-pool-3" #32 prio=5 os_prio=0 tid=0x... nid=0x4f2a runnable at com.xxx.Service.process(Service.java:88) at com.xxx.Service.loop(Service.java:75) // 卡在 while 里

nid=0x4f2a正好对应top -Hp里高 CPU 的线程,基本锁定:第 88 行那个 while 没有退出条件(或退出条件永远不满足)。修复加边界/超时即可。


四、实战:频繁 Full GC 导致飙高

如果top -Hp里高 CPU 的线程名是GC task thread#0G1 Concurrent Refinement之类,说明CPU 被 GC 吃掉了

路径就回到前面:看 GC 日志(-Xlog:gc*),老年代是不是一直在涨、Full GC 是不是很频繁。这本质是内存问题(第 10 篇),不是 CPU 问题——别在 CPU 上找错方向。


五、死锁排查

死锁比飙高更隐蔽:进程没崩、CPU 可能还很低,但某些请求永远不返回

jstack <PID>自动检测并直接打印死锁

Found one Java-level deadlock: ============================= "Thread-A": waiting to lock monitor 0x... (a java.lang.Object) which is held by "Thread-B" "Thread-B": waiting to lock monitor 0x... (a java.lang.Object) which is held by "Thread-A"

含义:线程 A 拿着锁 1 等锁 2,线程 B 拿着锁 2 等锁 1,互相等,永远卡住。

典型成因:synchronized嵌套且获取锁的顺序不一致。修法是所有线程按固定顺序获取锁,或改用tryLock(timeout)带超时。


六、Arthas 在线神器

上面六步要敲好几条命令、还要进制转换,生产紧张时容易出错。Arthas 一条命令解决:

  • thread -n 3:直接列出最忙的 3 个线程及其栈,死循环一眼看穿
  • thread -b直接找出死锁的线程(比 jstack 更友好)
  • dashboard:实时看 CPU/内存/线程总览

强烈建议把 Arthas 作为线上排查的「第一响应工具」。


七、预防

  1. 按固定顺序获取,避免交叉嵌套
  2. ReentrantLock.tryLock(timeout)替代无脑synchronized,超时可以释放而非死等
  3. 循环体加退出条件 + 超时保护,别写「理论上会退出」的死循环
  4. 限流 + 熔断,避免突发流量把 CPU 打满

八、锁竞争导致的飙高

还有一种常见但隐蔽的 CPU 高:没有明显死循环,但大量线程卡在lock/Unsafe.park上,上下文切换(context switch)暴增,CPU 花在「调度」而非「干活」。

topsy(系统态 CPU)偏高、vmstatcs(context switch)数值巨大,往往就是锁竞争。jstack里大量线程停在waiting to lock同一把锁。

解法:减小锁粒度(拆大锁)、用ConcurrentHashMap替代synchronized Map、降低共享可变状态。这本质是并发设计问题,不是 JVM 参数能解决的。


九、Arthas 实战一条龙

把前面六步浓缩成 Arthas 操作:

# 1. 启动并 attach 到目标进程java-jararthas-boot.jar# 2. 看最忙的 3 个线程(死循环直接现形)thread-n3# 3. 直接找死锁thread-b# 4. 看全局仪表盘dashboard# 5. 在线抓堆(等价于 jmap dump)heapdump /tmp/heap.hprof

相比手工top -Hp+printf+jstack,Arthas 把进制转换、线程定位、死锁检测全包了,生产急救效率提升一个量级。


十、一条经验法则

CPU 高 + 线程名带GC→ 内存问题(回第 9、10 篇)
CPU 高 + 单个业务线程 runnable 卡住 → 死循环(六步定位)
CPU 高 + 大量线程 waiting to lock → 锁竞争(拆锁)
CPU 不高但请求卡死 → 死锁(thread -b)

先定性、再定量,别一上来就瞎改代码。


十一、CPU 飙高根因决策表

把前面散落的知识点收成一张表,遇事对号入座:

现象可能根因下一步
单线程 runnable 卡住死循环jstack 定位代码行
线程名带 GC频繁 Full GC看 GC 日志(第 9 篇)
大量 waiting to lock锁竞争拆锁 / 换并发容器
线程数暴涨无界线程池改有界线程池(案例四)
CPU 不高但请求卡死锁thread -b 找死锁

先定性、再定量,别一上来就瞎改代码。


十二、容器里 CPU 限额导致的「假飙高」

云原生一个坑:容器设了cpu limit=2,但 JVM 启动时的ParallelGCThreads宿主机的核数(比如 32)初始化,于是 32 个 GC 线程抢 2 个 CPU 配额,被 CFS 调度限流,GC 反而更慢、业务也饿死,表现为 CPU 打满。

修复:用-XX:ParallelGCThreads显式设成容器配额对应的核数,或升级到能感知 cgroup 的 JDK 版本。这提醒我们:容器里的 JVM,参数要按「容器配额」而非「宿主机」来设


十三、CPU 飙高排查三字诀

总结成口诀方便记:「看、定、改」

  • :top 看进程、top -Hp 看线程、jstack/Arthas 看栈
  • :定是死循环、频繁 GC、还是锁竞争(用本文决策表)
  • :改循环边界、调堆/收集器、拆锁或换并发容器

口诀之外,最重要的是别慌——CPU 高只是表象,顺着工具链一层层剥,根因总会现形。

总结

CPU 飙高先分类(死循环 / 频繁 GC / 锁竞争),再走「top → top -Hp → printf → jstack」六步定位到代码行;死锁靠 jstack 的自动检测或 Arthasthread -b直接抓。记住:频繁 GC 引发的 CPU 高,根子在内存(回看第 9、10 篇),别在 CPU 上白费功夫。下一篇,我把整套 JVM 调优方法论收个尾。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 21:34:04

比ES快5倍的Meilisearch:中小规模全文搜索选型与迁移实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 21:32:40

微网群分布式调度:目标级联法(ATC)原理与MATLAB实现

1. 项目概述&#xff1a;微网群分布式调度的挑战与突破微网群作为新型电力系统的重要组成部分&#xff0c;正在经历从集中式控制向分布式协同的范式转变。传统集中式调度方法在面对多主体、高维度的微网群系统时&#xff0c;暴露出计算复杂度高、隐私保护不足和通信负担重等固有…

作者头像 李华
网站建设 2026/9/14 21:31:47

AI Agent辅助备份恢复演练——KFS MCP Server、步骤编排与风险控制

文章目录每日一句正能量前言1. 背景与问题2. 环境与数据3. 复现过程3.1 最简单的“假演练”3.2 只执行 pg_restore --list 也不够3.3 PITR 最常见问题&#xff1a;WAL 不连续3.4 “恢复成功”不等于业务可用4. 方案实施4.1 第一步&#xff1a;创建演练计划4.2 工具一&#xff1…

作者头像 李华
网站建设 2026/9/14 21:31:35

Lithe-IDEA:面向Java/Spring Boot开发的轻量级开源IDE

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华