监控报警内存 90%+排查记录

一、如何定位线上服务 CPU 飙升问题?——从进程到代码行的完整排查流程

最近天天邮件90%,真烦,决定撤底解决一下。本文记录排查流程,从进程 → 线程 → 代码堆栈,层层深入,最终锁定“元凶”。

本文适合有一定 Linux 运维基础、熟悉 Java 或其他 JVM/多线程语言的开发者阅读。


二、第一步:登录服务器,查看高 CPU 进程

使用 top 命令查看系统整体 CPU 使用情况:

top

在输出中,按 P(大写) 可按 CPU 使用率降序排列。你会看到类似:

PID   USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
12345 deploy    20   0 8542340 2.1g  18744 S 108.3 13.2   123:45 java

这里的关键信息:

  • PID = 12345:高 CPU 占用的进程 ID。
  • %CPU = 108.3:说明该进程使用了超过 1 个 CPU 核心(多线程并发)。

✅ 记下这个 PID,它是后续排查的起点。


三、第二步:查看该进程中高 CPU 的线程

一个进程包含多个线程。我们需要找出是哪个线程在“作妖”。

使用 ps 命令查看该进程下的所有线程及其 CPU 占用:

ps -mp 12345 -o THREAD,tid,time,%cpu

输出示例:

USER     TID %CPU  TIME
deploy  28512 15.2  12:34
deploy  28513  2.1   3:21
deploy  28514  0.5   1:02
...
  • TID = 28512:线程 ID(十进制)。
  • %CPU = 15.2%:该线程占用了大量 CPU。

✅ 找到 CPU 占用最高的 TID(如 28512)。


四、第三步:将 TID 转换为十六进制

JVM 的线程 dump(jstack)中,线程 ID 是以 十六进制 形式显示的。因此需要转换:

printf "%x\n" 28512

输出:

6f60

✅ 记下十六进制 TID:0x6f60(实际使用时去掉 0x,直接用 6f60)。


五、第四步:抓取线程堆栈,定位代码

使用 jstack(适用于 Java 应用)导出当前进程的线程快照:

jstack 12345 > jstack.log

然后在 jstack.log 中搜索十六进制 TID:

grep -A 30 "6f60" jstack.log

你会看到类似内容:

"Thread-123" #68 prio=5 os_prio=0 tid=0x00007f8a6f60 nid=0x6f60 runnable [0x00007f8a12345000]
   java.lang.Thread.State: RUNNABLE
        at com.example.service.DataProcessor.process(DataProcessor.java:45)
        at com.example.service.DataProcessor.lambda$handle$0(DataProcessor.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

🎯 关键信息:

  • 线程名:Thread-123
  • 代码位置:DataProcessor.java 第 45 行
  • 状态:RUNNABLE(正在运行,可能死循环或高频计算)

六、分析可能原因

常见导致 CPU 飙升的原因包括:

  • 死循环(如 while(true) 忘记 break)
  • 高频 I/O 或反射操作(如频繁 JSON 序列化)
  • 正则表达式回溯爆炸
  • HashMap 在高并发下形成死链(旧版 JDK)
  • 自旋锁或 busy-wait 逻辑

在你的代码中,重点检查堆栈顶部的方法逻辑。


七、补充:非 Java 应用怎么办?

  • Go/C++/Python:可用 perf 工具采样:
    perf top -p 12345
    
    或生成火焰图(Flame Graph)分析热点函数。
  • 通用方法strace -p <PID> 查看系统调用,但可能影响性能,慎用。

总结:排查流程图

CPU 飙升报警
     ↓
top → 找到高 CPU PID
     ↓
ps -mp <PID> → 找到高 CPU TID(十进制)
     ↓
printf "%x" <TID> → 转十六进制
     ↓
jstack <PID> | grep <hex TID> → 定位代码行
     ↓
修复代码 + 压测验证

示例场景回顾

假设你发现:

  • PID = 285
  • TID = 28512(十进制)→ 十六进制 = 6f60
  • jstack 显示线程卡在 FileReader.read() 死循环

那你就可以立即检查该读取逻辑,很可能是未正确处理 EOF 或缓冲区。


希望这篇整理能帮助大家在面对 CPU 飙升问题时,不再手忙脚乱。性能问题不可怕,可怕的是没有排查路径。掌握这套方法,你就能在“甜蜜约会”之余,依然做团队中最稳的救火队员 💪!

欢迎在评论区分享你的排查经历,或者提出更复杂的场景,我们一起攻克!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值