# 线上业务服务器 CPU 飙高如何排查?

8 min read
Table of Contents

线上业务服务器 CPU 飙高如何排查?

线上业务服务器 CPU 飙高排查指南

【核心定义】

线上服务器 CPU 飙高排查是一套通过系统监控、进程定位、线程分析、堆栈诊断的标准化流程,旨在快速定位导致 CPU 资源异常消耗的进程、线程及具体代码逻辑。

【关键要点】

  1. 全局监控与初步定位:使用 top 或 htop 命令,按 CPU 使用率排序,快速定位是哪个进程(PID)消耗了过高的 CPU。
  2. 线程级分析:通过 top -Hp <PID> 或 ps -mp <PID> -o THREAD,tid,time 命令,查看目标进程内各个线程的 CPU 使用情况,找到问题线程的 ID(TID)。
  3. 堆栈信息获取:将十进制的 TID 转换为十六进制,使用 jstack <PID> | grep -A 20 <nid> 或 jstack <PID> > jstack.log 分析线程堆栈,定位正在执行的代码行。
  4. 结合其他工具交叉验证:使用 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 高更有优势。

『面试官追问』

  1. 如果 jstack 看不到明显的死循环线程怎么办?
    • 答:可能是瞬时高峰未被捕获。应使用连续抓取多份 jstack 日志对比分析,或使用 Arthas profiler 生成火焰图,可视化地看到 CPU 时间片在方法上的分布。也可能是 GC 问题,需用 jstat -gcutil <PID> 1000 每隔1秒观察 Eden、Old 区变化和 GC 次数/时间。
  2. 如何排查因为频繁 Full GC 导致的 CPU 飙高?
    • 答:这是典型关联场景。步骤是:1) jstat 确认 Full GC 频率和时间;2) 若频繁,使用 jmap -dump:live,format=b,file=heap.hprof <PID> 获取堆转储(对线上影响大,需谨慎);3) 使用 MAT 或 JVisualVM 分析堆转储,查找占据大量内存的对象和引用链,定位内存泄漏点。
  3. 除了 Java 应用本身,还有哪些系统层面原因可能导致 CPU 高?
    • 答:1) 其他用户进程:被入侵挖矿;2) 内核线程:使用 ps aux | grep -E “\[.*\]” 查看内核线程;3) 中断过高:使用 cat /proc/interrupts 或 vmstat 查看 in 列,可能是网络或磁盘硬件问题;4) 上下文切换过多:vmstat 中 cs 值巨大,可能是线程数过多或锁竞争激烈。

【直击痛点与最佳实践】

  • 痛点:生产环境不敢轻易执行 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)。
My avatar

文章看完了,不打算去别的篇章转转吗?👀 感谢你的耐心阅读!如果你有问题想探讨,或者发现了文中的 Bug,请务必通过页脚的链接联系我


More Posts

Comments