# 线上业务服务器 CPU 飙高如何排查?
8 min read
Table of Contents
线上业务服务器 CPU 飙高如何排查?
线上业务服务器 CPU 飙高排查指南
【核心定义】
线上服务器 CPU 飙高排查是一套通过系统监控、进程定位、线程分析、堆栈诊断的标准化流程,旨在快速定位导致 CPU 资源异常消耗的进程、线程及具体代码逻辑。
【关键要点】
- 全局监控与初步定位:使用
top或htop命令,按 CPU 使用率排序,快速定位是哪个进程(PID)消耗了过高的 CPU。 - 线程级分析:通过
top -Hp <PID>或ps -mp <PID> -o THREAD,tid,time命令,查看目标进程内各个线程的 CPU 使用情况,找到问题线程的 ID(TID)。 - 堆栈信息获取:将十进制的 TID 转换为十六进制,使用
jstack <PID> | grep -A 20 <nid>或jstack <PID> > jstack.log分析线程堆栈,定位正在执行的代码行。 - 结合其他工具交叉验证:使用
vmstat、sar查看系统整体状态,使用jstat查看 GC 情况,使用Arthas的thread、profiler命令进行在线诊断。
【深度推导/细节】
排查流程本质是从宏观到微观的逐层下钻,核心矛盾在于如何在复杂的线上环境中,以最小侵入性快速找到“元凶”。
逻辑拆解步骤:
- Step 1: 确认现象与影响范围。是单台机器偶发还是集群普遍现象?这决定了是局部问题还是应用代码或依赖服务(如数据库、缓存)的共性问题。
- Step 2: 定位问题进程。使用
top命令,观察%CPU和TIME+字段。如果是 Java 进程,通常嫌疑最大。 - Step 3: 定位问题线程。一个高 CPU 的 Java 进程,通常是因为其内部一个或几个线程在“疯狂运行”。使用
top -Hp <PID>找到高耗 CPU 的线程 ID。 - Step 4: 分析线程堆栈。这是最关键的一步。将 TID 转为十六进制(
printf “%x\n” <TID>),然后在jstack输出的线程快照中搜索对应的nid。观察该线程的堆栈,常见的模式有:- 死循环:堆栈停留在某个循环方法内。
- 频繁 GC:可能看到大量的
GC task线程,结合jstat -gcutil <PID>确认,这通常由内存泄漏或不当的 GC 参数引起。 - 锁竞争:线程状态为
BLOCKED或WAITING,且长时间不释放,可能导致其他线程空转(但通常这更可能导致 CPU 使用率不高而性能差,极端竞争下 CPU 也会高)。
- Step 5: 关联代码与数据。根据堆栈指明的类和方法,结合业务日志、近期发布记录,分析触发异常逻辑的上下文数据。
【关联/对比】
- CPU 飙高 vs 内存泄漏:CPU 飙高通常与计算密集型操作(循环、正则、序列化)或频繁的线程调度/GC有关;内存泄漏则表现为
%MEM持续增长,最终可能引发频繁 Full GC 间接导致 CPU 高。排查工具上,后者更依赖jmap、jstat和堆转储分析。 - 用户态 CPU 高 vs 系统态 CPU 高:
top命令中%us高通常是应用代码问题;%sy高则可能与系统调用频繁、线程上下文切换过多(可用vmstat 1查看cs值)或底层资源(如虚拟化层)竞争有关。 - 传统命令 vs 在线诊断工具:
jstack是瞬时快照,可能抓不到瞬间的峰值。而像 Arthas 这样的在线诊断工具,可以提供连续采样(profiler)、监控特定方法耗时(watch/trace),对排查偶发、瞬时 CPU 高更有优势。
『面试官追问』
- 如果
jstack看不到明显的死循环线程怎么办?- 答:可能是瞬时高峰未被捕获。应使用连续抓取多份
jstack日志对比分析,或使用Arthas profiler生成火焰图,可视化地看到 CPU 时间片在方法上的分布。也可能是 GC 问题,需用jstat -gcutil <PID> 1000每隔1秒观察 Eden、Old 区变化和 GC 次数/时间。
- 答:可能是瞬时高峰未被捕获。应使用连续抓取多份
- 如何排查因为频繁 Full GC 导致的 CPU 飙高?
- 答:这是典型关联场景。步骤是:1)
jstat确认 Full GC 频率和时间;2) 若频繁,使用jmap -dump:live,format=b,file=heap.hprof <PID>获取堆转储(对线上影响大,需谨慎);3) 使用 MAT 或 JVisualVM 分析堆转储,查找占据大量内存的对象和引用链,定位内存泄漏点。
- 答:这是典型关联场景。步骤是:1)
- 除了 Java 应用本身,还有哪些系统层面原因可能导致 CPU 高?
- 答:1) 其他用户进程:被入侵挖矿;2) 内核线程:使用
ps aux | grep -E “\[.*\]”查看内核线程;3) 中断过高:使用cat /proc/interrupts或vmstat查看in列,可能是网络或磁盘硬件问题;4) 上下文切换过多:vmstat中cs值巨大,可能是线程数过多或锁竞争激烈。
- 答:1) 其他用户进程:被入侵挖矿;2) 内核线程:使用
【直击痛点与最佳实践】
- 痛点:生产环境不敢轻易执行
jmap或重启。- 对策:优先使用低侵入性命令(
top,jstack,jstat)完成初步定位。提前搭建监控告警体系(如 Prometheus + Grafana 监控 JVM 指标),在 CPU 开始爬升时就收到警报,而非等到 100%。推广使用 Arthas 进行在线诊断,其profiler和thread命令对性能影响极小。
- 对策:优先使用低侵入性命令(
- 痛点:堆栈信息看不懂或没有业务代码。
- 对策:堆栈中看到
java.lang.Thread.run、java.util.concurrent.ThreadPoolExecutor$Worker.run是线程池框架代码,需要向上找到提交到线程池的Runnable或Callable任务类。看到GC task线程则直接转向 GC 问题排查。确保线上代码保留调试符号(不要用-g:none编译)。
- 对策:堆栈中看到
- 标准化流程:为团队制定《线上故障排查手册》,将 CPU/内存/磁盘 IO 等常见问题的排查步骤、命令、工具 SOP 化,并定期进行演练,能极大缩短故障恢复时间(MTTR)。