Linux 系统 Bug 调试操作手册

Linux 系统 Bug 调试操作手册

适用环境 :Ubuntu 20.04 / 22.04 / 24.04(systemd 体系)

目标读者 :有基础 Linux 经验的后端开发,目标是能独立定位中低复杂度问题

覆盖问题 :崩溃(Crash)、挂起(Hang)、OOM、性能劣化、服务启动失败

工具原则:优先系统自带工具(procps / iproute2 / systemd / util-linux),少量常用诊断工具通过官方 apt 源安装,不依赖任何第三方商业软件

使用约定

  • 每个命令后跟一行 「看什么」,说明输出里该盯住的关键字或数值。
  • 命令中的 <svc> 指 systemd 服务名,<PID> 指进程号,按实际替换。
  • 排查顺序永远是:先保现场 → 再看资源 → 后动进程。没采集信息就重启,等于销毁证据。
  • 正文含 6 张 Mermaid 流程图(总决策树 + 五类问题专项流程)。GitHub / GitLab / Typora 原生渲染;VS Code 需装 Mermaid 预览插件。

1. 信息采集清单(到场后前 60 秒)

不管什么问题,先跑一遍下面的快照命令。输出本身就是第一手证据,也能让后续排查少绕路。

# 命令 用途 预期输出里的关键字
1 uptime 总览负载与运行时长 load average: 三个数是否递增;是否超过 CPU 核数
2 free -h 内存与 swap 概况 available 是否接近 0;Swap 是否用满
3 df -h 磁盘空间 是否有分区 Use% 100%
4 df -i inode 使用率 IUse% 是否 100%(空间没满也写不进文件)
5 vmstat 1 5 CPU / 内存 / 换页动态趋势 r/b 列、us sy wa stsi so
6 `dmesg -T tail -50` 内核近期消息(带时间戳)
7 `journalctl -p 3 -xb tail -50` 本次开机以来 error 级日志
8 systemctl --failed 处于失败状态的 unit 失败服务列表
9 `ps aux --sort=-%mem head -15` 内存占用 Top 进程
10 `ps aux --sort=-%cpu head -15` CPU 占用 Top 进程
11 ss -lntp 监听端口与对应进程 预期端口是否都在;有无意外监听
12 ss -s 连接状态汇总 timewait / close-wait 数量是否异常

一次性快照(可直接粘贴执行):

bash 复制代码
uptime; free -h; df -h; df -i; ss -s; dmesg -T | tail -30

看什么:一眼扫过去是否已有明显异常------磁盘满、内存空、连接堆积、内核报错,任何一条命中就直接指向对应章节。

补充:如果机器装了 sysstat(见 §4),历史数据是"案发当时"的唯一证人:

bash 复制代码
sar -u -f /var/log/sysstat/sa$(date -d yesterday +%d)

看什么:昨天同时段的 CPU 曲线,和今天对比,确认"劣化"是从哪一刻开始的。


2. 分层排查流程图(决策树)

按照「内核层 → 系统资源层 → 服务层 → 代码层」逐层缩小范围:

text 复制代码
故障现象(告警 / 用户反馈 / 巡检发现)
│
├─ 第 1 步:保护现场,跑 §1 信息采集清单
│
├─ 第 2 步:进程还在吗?
│   命令:ps aux | grep <进程名>  或  systemctl status <svc>
│   │
│   ├─ 【不在了 / 反复重启中】
│   │   ├─ journalctl -k | grep -i 'out of memory' 有命中?
│   │   │   ├─ 有 ────────────► OOM 专线 ──────► §3.3
│   │   │   └─ 无 ↓
│   │   ├─ dmesg / journalctl -k 里有 segfault / signal 记录?
│   │   │   ├─ 有 ────────────► 崩溃专线 ──────► §3.1
│   │   │   └─ 无 ↓
│   │   └─ 退出码非 0、进程自行退出
│   │       └─────────────────► 启动失败专线 ──► §3.5
│   │
│   └─ 【还在,但行为异常(超时 / 无响应 / 变慢)】
│       │
│       ├─ 整机忙吗?看 uptime + vmstat 1
│       │   ├─ load 高、r 列大、us+sy 高 ─► CPU 瓶颈 ────┐
│       │   ├─ wa 高、b 列大、iostat %util≈100 ► IO 瓶颈 ─┼► 性能劣化专线 §3.4
│       │   ├─ si/so 持续非 0 ─► swap 换页,内存压力 ─────┘(并参考 §3.3)
│       │   └─ 整机不忙,只有单个服务异常 ↓
│       │
│       └─ 进程"活着但不干活"?
│           ├─ 连接堆积:大量 CLOSE_WAIT / Recv-Q 积压 ─► 挂起专线 §3.2
│           ├─ 线程大量处于 D 状态(不可中断睡眠)─────► 挂起专线 §3.2
│           └─ 只是部分请求慢 ─────────────────────────► 性能劣化专线 §3.4
│
└─ 第 3 步:单服务问题还是全局问题?
    ├─ 单服务 → 重点在应用层:日志 → strace/gdb → core dump
    └─ 全机器 → 重点在系统层:按 USE 方法扫一遍
               每种资源(CPU/内存/磁盘/网络)查三件事:
               使用率(Utilization)、饱和度(Saturation)、错误数(Errors)

决策树可视化版(Mermaid):
#mermaid-svg-2qRVdlVYKqkHfogS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2qRVdlVYKqkHfogS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2qRVdlVYKqkHfogS .error-icon{fill:#552222;}#mermaid-svg-2qRVdlVYKqkHfogS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2qRVdlVYKqkHfogS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2qRVdlVYKqkHfogS .marker.cross{stroke:#333333;}#mermaid-svg-2qRVdlVYKqkHfogS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2qRVdlVYKqkHfogS p{margin:0;}#mermaid-svg-2qRVdlVYKqkHfogS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2qRVdlVYKqkHfogS .cluster-label text{fill:#333;}#mermaid-svg-2qRVdlVYKqkHfogS .cluster-label span{color:#333;}#mermaid-svg-2qRVdlVYKqkHfogS .cluster-label span p{background-color:transparent;}#mermaid-svg-2qRVdlVYKqkHfogS .label text,#mermaid-svg-2qRVdlVYKqkHfogS span{fill:#333;color:#333;}#mermaid-svg-2qRVdlVYKqkHfogS .node rect,#mermaid-svg-2qRVdlVYKqkHfogS .node circle,#mermaid-svg-2qRVdlVYKqkHfogS .node ellipse,#mermaid-svg-2qRVdlVYKqkHfogS .node polygon,#mermaid-svg-2qRVdlVYKqkHfogS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2qRVdlVYKqkHfogS .rough-node .label text,#mermaid-svg-2qRVdlVYKqkHfogS .node .label text,#mermaid-svg-2qRVdlVYKqkHfogS .image-shape .label,#mermaid-svg-2qRVdlVYKqkHfogS .icon-shape .label{text-anchor:middle;}#mermaid-svg-2qRVdlVYKqkHfogS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2qRVdlVYKqkHfogS .rough-node .label,#mermaid-svg-2qRVdlVYKqkHfogS .node .label,#mermaid-svg-2qRVdlVYKqkHfogS .image-shape .label,#mermaid-svg-2qRVdlVYKqkHfogS .icon-shape .label{text-align:center;}#mermaid-svg-2qRVdlVYKqkHfogS .node.clickable{cursor:pointer;}#mermaid-svg-2qRVdlVYKqkHfogS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2qRVdlVYKqkHfogS .arrowheadPath{fill:#333333;}#mermaid-svg-2qRVdlVYKqkHfogS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2qRVdlVYKqkHfogS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2qRVdlVYKqkHfogS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2qRVdlVYKqkHfogS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2qRVdlVYKqkHfogS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2qRVdlVYKqkHfogS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2qRVdlVYKqkHfogS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2qRVdlVYKqkHfogS .cluster text{fill:#333;}#mermaid-svg-2qRVdlVYKqkHfogS .cluster span{color:#333;}#mermaid-svg-2qRVdlVYKqkHfogS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2qRVdlVYKqkHfogS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2qRVdlVYKqkHfogS rect.text{fill:none;stroke-width:0;}#mermaid-svg-2qRVdlVYKqkHfogS .icon-shape,#mermaid-svg-2qRVdlVYKqkHfogS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2qRVdlVYKqkHfogS .icon-shape p,#mermaid-svg-2qRVdlVYKqkHfogS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2qRVdlVYKqkHfogS .icon-shape .label rect,#mermaid-svg-2qRVdlVYKqkHfogS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2qRVdlVYKqkHfogS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2qRVdlVYKqkHfogS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2qRVdlVYKqkHfogS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 不在了 / 反复重启



无,退出码非 0
还在但异常
r 列大,us+sy 高
wa 高,b 列大
si/so 持续非 0
整机不忙
CLOSE-WAIT 堆积 / 大量 D 状态线程
只是部分请求慢
故障出现(告警 / 反馈 / 巡检)
§1 信息采集快照

uptime · free · df · dmesg · journalctl
进程还在吗?

ps aux · systemctl status
journalctl -k 命中

Out of memory?
§3.3 OOM 专线
dmesg / journalctl -k 有

segfault / signal 记录?
§3.1 崩溃专线
§3.5 启动失败专线
整机忙吗?

uptime + vmstat 1
CPU 瓶颈 → §3.4
IO 瓶颈 → §3.4
内存压力 → §3.3
进程活着但不干活?
§3.2 挂起专线
§3.4 性能劣化专线

决策树使用说明

  1. 从树根往下走,每一步只用一条命令做判断,不要跳步。
  2. 走到叶子节点后进入 §3 对应专线;专线内仍然遵循「现象 → 工具 → 命令 → 判断依据」的节奏。
  3. 多个现象并存时(例如又慢又重启),先查"进程不在"这条线------崩溃/OOM 是根因,慢只是后果。

3. 五类问题专项排查步骤

3.1 进程崩溃(Crash)

现象

  • systemctl status 显示 Main process exited, code=killed, status=11/SEGV(或 6/ABRT)
  • 服务反复 activating(auto-restart),重启计数持续增长
  • 监控上进程存活曲线呈锯齿状;客户端看到连接被重置

工具journalctldmesgcoredumpctlgdb

排查流程图
#mermaid-svg-ptiVJZFwARh8xxht{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ptiVJZFwARh8xxht .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ptiVJZFwARh8xxht .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ptiVJZFwARh8xxht .error-icon{fill:#552222;}#mermaid-svg-ptiVJZFwARh8xxht .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ptiVJZFwARh8xxht .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ptiVJZFwARh8xxht .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ptiVJZFwARh8xxht .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ptiVJZFwARh8xxht .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ptiVJZFwARh8xxht .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ptiVJZFwARh8xxht .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ptiVJZFwARh8xxht .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ptiVJZFwARh8xxht .marker.cross{stroke:#333333;}#mermaid-svg-ptiVJZFwARh8xxht svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ptiVJZFwARh8xxht p{margin:0;}#mermaid-svg-ptiVJZFwARh8xxht .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ptiVJZFwARh8xxht .cluster-label text{fill:#333;}#mermaid-svg-ptiVJZFwARh8xxht .cluster-label span{color:#333;}#mermaid-svg-ptiVJZFwARh8xxht .cluster-label span p{background-color:transparent;}#mermaid-svg-ptiVJZFwARh8xxht .label text,#mermaid-svg-ptiVJZFwARh8xxht span{fill:#333;color:#333;}#mermaid-svg-ptiVJZFwARh8xxht .node rect,#mermaid-svg-ptiVJZFwARh8xxht .node circle,#mermaid-svg-ptiVJZFwARh8xxht .node ellipse,#mermaid-svg-ptiVJZFwARh8xxht .node polygon,#mermaid-svg-ptiVJZFwARh8xxht .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ptiVJZFwARh8xxht .rough-node .label text,#mermaid-svg-ptiVJZFwARh8xxht .node .label text,#mermaid-svg-ptiVJZFwARh8xxht .image-shape .label,#mermaid-svg-ptiVJZFwARh8xxht .icon-shape .label{text-anchor:middle;}#mermaid-svg-ptiVJZFwARh8xxht .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ptiVJZFwARh8xxht .rough-node .label,#mermaid-svg-ptiVJZFwARh8xxht .node .label,#mermaid-svg-ptiVJZFwARh8xxht .image-shape .label,#mermaid-svg-ptiVJZFwARh8xxht .icon-shape .label{text-align:center;}#mermaid-svg-ptiVJZFwARh8xxht .node.clickable{cursor:pointer;}#mermaid-svg-ptiVJZFwARh8xxht .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ptiVJZFwARh8xxht .arrowheadPath{fill:#333333;}#mermaid-svg-ptiVJZFwARh8xxht .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ptiVJZFwARh8xxht .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ptiVJZFwARh8xxht .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ptiVJZFwARh8xxht .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ptiVJZFwARh8xxht .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ptiVJZFwARh8xxht .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ptiVJZFwARh8xxht .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ptiVJZFwARh8xxht .cluster text{fill:#333;}#mermaid-svg-ptiVJZFwARh8xxht .cluster span{color:#333;}#mermaid-svg-ptiVJZFwARh8xxht div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ptiVJZFwARh8xxht .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ptiVJZFwARh8xxht rect.text{fill:none;stroke-width:0;}#mermaid-svg-ptiVJZFwARh8xxht .icon-shape,#mermaid-svg-ptiVJZFwARh8xxht .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ptiVJZFwARh8xxht .icon-shape p,#mermaid-svg-ptiVJZFwARh8xxht .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ptiVJZFwARh8xxht .icon-shape .label rect,#mermaid-svg-ptiVJZFwARh8xxht .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ptiVJZFwARh8xxht .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ptiVJZFwARh8xxht .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ptiVJZFwARh8xxht :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 有
没有
业务代码
第三方库
只有地址没有符号
进程消失 / 反复重启
systemctl status

确认 code=killed 与信号编号
journalctl -k 找 segfault 行

确认崩溃模块
coredumpctl list

有 core dump 吗?
coredumpctl gdb 加载 core

bt / bt full 看调用栈
开启 core 生成:

ulimit -c unlimited

确认 core_pattern 后复现
栈顶在哪?
修代码:野指针 / 越界 / 断言失败
查该库已知 bug

升级版本或校验输入
安装 dbgsym 调试符号

再重新分析

排查命令

bash 复制代码
systemctl status <svc> -l --no-pager

看什么Main PID 的退出方式------code=killed, status=11/SEGV 是段错误,status=6/ABRT 是程序自己 abort;以及日志最后几行的遗言。

bash 复制代码
journalctl -u <svc> -n 200 --no-pager

看什么 :崩溃前最后的输出:异常栈、断言信息(Assertion failed)、double freecorrupted size 等字样。

bash 复制代码
journalctl -k -b | grep -iE 'segfault|oops|panic|traps:'

看什么segfault at <地址> ip <指令地址> ... in <模块名> ------崩溃发生在哪个二进制或 .so 里;error 标志位(4=用户态读访问缺页)。

bash 复制代码
coredumpctl list

看什么 :有没有对应时间的 core dump 记录;TIMEPID 是否与事故对得上。

bash 复制代码
coredumpctl info <PID或程序名>

看什么:崩溃信号编号:SIGSEGV=11 / SIGABRT=6 / SIGBUS=7 / SIGFPE=8。

bash 复制代码
coredumpctl gdb <程序名>

看什么:自动加载 core 文件进入 gdb,接下来在 gdb 里看调用栈。

gdb 内部命令:

gdb 复制代码
bt
bt full
info sharedlibrary

看什么bt 栈顶函数是崩在哪------业务代码还是第三方库;bt full 看局部变量是否为野值;info sharedlibrary 确认 so 是否缺符号(缺符号时栈里只有地址没有函数名)。

判断依据

信号 常见原因 下一步
SIGSEGV (11) 野指针、越界读写、栈溢出 bt 栈顶 + 变量值
SIGABRT (6) 程序主动 abort:断言失败、glibc 检测到堆损坏(double free) 重点查内存操作,开 AddressSanitizer 复现
SIGBUS (7) 内存对齐错误、访问被截断的 mmap 文件 查磁盘是否写满导致 mmap 文件被截断
SIGFPE (8) 除零 看栈顶除法的除数来源

core 文件没生成时

bash 复制代码
ulimit -c unlimited

看什么:当前 shell 允许生成的 core 大小上限,为 0 表示被限制。

bash 复制代码
cat /proc/sys/kernel/core_pattern

看什么 :core 的去向。Ubuntu 默认以 |/usr/lib/systemd/systemd-coredump ...|/usr/share/apport/apport ... 形式被接管,所以用 coredumpctl 查,而不是去进程工作目录找文件。

需要临时把 core 落成文件时(root):

bash 复制代码
echo '/tmp/core.%e.%p' > /proc/sys/kernel/core_pattern

看什么 :复现后 /tmp 下出现 core.<程序名>.<pid>,用 gdb <程序> /tmp/core.xxx 分析;查完记得改回原值。

提示 :gdb 里全是十六进制地址没有函数名 → 安装调试符号:apt install <包名>-dbgsym(或 <包名>-dbg)。


3.2 进程挂起 / 无响应(Hang)

现象

  • 进程在、端口在,但请求全部超时;重启后短暂恢复
  • load 缓慢上涨(请求堆积),但 CPU 利用率不高
  • ss -tn 里大量 CLOSE-WAIT,或 Recv-Q 积压不降

工具psssstracegdb/proc/<PID>/stack

排查流程图
#mermaid-svg-Kyf7goyCs12oVZDM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Kyf7goyCs12oVZDM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Kyf7goyCs12oVZDM .error-icon{fill:#552222;}#mermaid-svg-Kyf7goyCs12oVZDM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Kyf7goyCs12oVZDM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Kyf7goyCs12oVZDM .marker.cross{stroke:#333333;}#mermaid-svg-Kyf7goyCs12oVZDM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Kyf7goyCs12oVZDM p{margin:0;}#mermaid-svg-Kyf7goyCs12oVZDM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Kyf7goyCs12oVZDM .cluster-label text{fill:#333;}#mermaid-svg-Kyf7goyCs12oVZDM .cluster-label span{color:#333;}#mermaid-svg-Kyf7goyCs12oVZDM .cluster-label span p{background-color:transparent;}#mermaid-svg-Kyf7goyCs12oVZDM .label text,#mermaid-svg-Kyf7goyCs12oVZDM span{fill:#333;color:#333;}#mermaid-svg-Kyf7goyCs12oVZDM .node rect,#mermaid-svg-Kyf7goyCs12oVZDM .node circle,#mermaid-svg-Kyf7goyCs12oVZDM .node ellipse,#mermaid-svg-Kyf7goyCs12oVZDM .node polygon,#mermaid-svg-Kyf7goyCs12oVZDM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Kyf7goyCs12oVZDM .rough-node .label text,#mermaid-svg-Kyf7goyCs12oVZDM .node .label text,#mermaid-svg-Kyf7goyCs12oVZDM .image-shape .label,#mermaid-svg-Kyf7goyCs12oVZDM .icon-shape .label{text-anchor:middle;}#mermaid-svg-Kyf7goyCs12oVZDM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Kyf7goyCs12oVZDM .rough-node .label,#mermaid-svg-Kyf7goyCs12oVZDM .node .label,#mermaid-svg-Kyf7goyCs12oVZDM .image-shape .label,#mermaid-svg-Kyf7goyCs12oVZDM .icon-shape .label{text-align:center;}#mermaid-svg-Kyf7goyCs12oVZDM .node.clickable{cursor:pointer;}#mermaid-svg-Kyf7goyCs12oVZDM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Kyf7goyCs12oVZDM .arrowheadPath{fill:#333333;}#mermaid-svg-Kyf7goyCs12oVZDM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Kyf7goyCs12oVZDM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Kyf7goyCs12oVZDM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Kyf7goyCs12oVZDM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Kyf7goyCs12oVZDM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Kyf7goyCs12oVZDM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Kyf7goyCs12oVZDM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Kyf7goyCs12oVZDM .cluster text{fill:#333;}#mermaid-svg-Kyf7goyCs12oVZDM .cluster span{color:#333;}#mermaid-svg-Kyf7goyCs12oVZDM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Kyf7goyCs12oVZDM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Kyf7goyCs12oVZDM rect.text{fill:none;stroke-width:0;}#mermaid-svg-Kyf7goyCs12oVZDM .icon-shape,#mermaid-svg-Kyf7goyCs12oVZDM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Kyf7goyCs12oVZDM .icon-shape p,#mermaid-svg-Kyf7goyCs12oVZDM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Kyf7goyCs12oVZDM .icon-shape .label rect,#mermaid-svg-Kyf7goyCs12oVZDM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Kyf7goyCs12oVZDM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Kyf7goyCs12oVZDM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Kyf7goyCs12oVZDM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 大量 D 状态
io_schedule
nfs_ 相关
单线程 100% CPU
所有线程接近 0%
futex
read socket / pipe
进程在、端口在,请求超时
ps -o stat / top -H -p

看线程状态与每线程 CPU
线程状态形态?
cat /proc/PID/stack

看内核阻塞点
栈里关键字?
磁盘 / 文件系统卡住

转 §3.4 IO 方向
NFS 挂载问题
strace 几乎无输出 = 用户态死循环

用 TID 在 gdb 中定位栈
strace -p -tt -T

看停在哪条调用
停在哪?
锁竞争 / 死锁

gdb: thread apply all bt
等上游数据

排查对端服务

排查命令

bash 复制代码
ps -o pid,stat,wchan:32,cmd -p <PID>

看什么STAT 列------D 表示不可中断睡眠(通常在等 IO),S 是可中断睡眠;WCHAN 是阻塞点的内核函数名(如 io_schedulepipe_wait)。

bash 复制代码
cat /proc/<PID>/stack

看什么 :内核态调用栈:出现 io_schedule → 在等磁盘;nfs_* → 卡在 NFS;blk_mq_* → 块设备层排队。

bash 复制代码
ss -tnp | grep <PID>

看什么 :大量 CLOSE-WAIT = 应用没有关闭连接(代码问题);Recv-Q 大 = 数据读不走;Send-Q 大 = 对端不收或网络拥塞。

bash 复制代码
top -H -p <PID>

看什么 :线程级 CPU------单线程 100% 多为死循环/自旋锁;全部线程接近 0% 说明在等锁或等外部输入。记下可疑线程的 TID(top -H 显示的 PID 列即 TID)。

bash 复制代码
strace -p <PID> -tt -T -f

看什么 :卡在哪条系统调用上:停在 futex(...) = 等锁/条件变量;停在 read(fd) = 等上游或文件;-tt 显示微秒时间戳,-T 显示每条调用耗时。观察完尽快 Ctrl+C 退出,strace 本身有明显开销。

bash 复制代码
timeout 10 strace -p <PID> -c

看什么 :10 秒系统调用统计汇总------futex 次数和耗时占比高 → 锁竞争;read/write 为主 → 正常 IO 路径,转性能方向。

bash 复制代码
gdb -p <PID>
gdb 复制代码
thread apply all bt
detach
quit

看什么 :所有线程的用户态调用栈------多个线程停在同一把锁(pthread_mutex_lock / futex)→ 热点锁或死锁;单线程停在某个业务函数 → 逻辑问题(死循环、无限重试)。gdb attach 会短暂冻结进程,生产上用完立即 detach。

判断依据

证据组合 结论
strace 停 futex + 线程全 0% CPU + 栈都卡同一把锁 应用内死锁 / 长临界区 → 查加锁顺序
线程 D 状态 + /proc/<PID>/stackio_schedule 磁盘 / 文件系统把进程拖住了 → 转 §3.4 IO 方向,并查磁盘健康
单线程 100% CPU + strace 几乎无输出 用户态死循环 → 用 TID 在 gdb 里定位该线程的栈
大量 CLOSE-WAIT + 应用线程都在 read socket 上游不返回数据 → 问题在对端,查上游服务

3.3 OOM(内存耗尽被杀)

现象

  • 进程突然消失,应用自己的日志里没有任何崩溃栈
  • 系统日志出现 Out of memory: Killed process
  • 机器先整体变慢(swap 疯狂换页),随后某进程被杀

工具journalctl -kfreepspmapslabtopsystemd-cgtop

排查流程图
#mermaid-svg-AR1uvkLbQVPDdkkj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AR1uvkLbQVPDdkkj .error-icon{fill:#552222;}#mermaid-svg-AR1uvkLbQVPDdkkj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AR1uvkLbQVPDdkkj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AR1uvkLbQVPDdkkj .marker.cross{stroke:#333333;}#mermaid-svg-AR1uvkLbQVPDdkkj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AR1uvkLbQVPDdkkj p{margin:0;}#mermaid-svg-AR1uvkLbQVPDdkkj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster-label text{fill:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster-label span{color:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster-label span p{background-color:transparent;}#mermaid-svg-AR1uvkLbQVPDdkkj .label text,#mermaid-svg-AR1uvkLbQVPDdkkj span{fill:#333;color:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj .node rect,#mermaid-svg-AR1uvkLbQVPDdkkj .node circle,#mermaid-svg-AR1uvkLbQVPDdkkj .node ellipse,#mermaid-svg-AR1uvkLbQVPDdkkj .node polygon,#mermaid-svg-AR1uvkLbQVPDdkkj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AR1uvkLbQVPDdkkj .rough-node .label text,#mermaid-svg-AR1uvkLbQVPDdkkj .node .label text,#mermaid-svg-AR1uvkLbQVPDdkkj .image-shape .label,#mermaid-svg-AR1uvkLbQVPDdkkj .icon-shape .label{text-anchor:middle;}#mermaid-svg-AR1uvkLbQVPDdkkj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AR1uvkLbQVPDdkkj .rough-node .label,#mermaid-svg-AR1uvkLbQVPDdkkj .node .label,#mermaid-svg-AR1uvkLbQVPDdkkj .image-shape .label,#mermaid-svg-AR1uvkLbQVPDdkkj .icon-shape .label{text-align:center;}#mermaid-svg-AR1uvkLbQVPDdkkj .node.clickable{cursor:pointer;}#mermaid-svg-AR1uvkLbQVPDdkkj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AR1uvkLbQVPDdkkj .arrowheadPath{fill:#333333;}#mermaid-svg-AR1uvkLbQVPDdkkj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AR1uvkLbQVPDdkkj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AR1uvkLbQVPDdkkj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AR1uvkLbQVPDdkkj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AR1uvkLbQVPDdkkj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AR1uvkLbQVPDdkkj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster text{fill:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster span{color:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AR1uvkLbQVPDdkkj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj rect.text{fill:none;stroke-width:0;}#mermaid-svg-AR1uvkLbQVPDdkkj .icon-shape,#mermaid-svg-AR1uvkLbQVPDdkkj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AR1uvkLbQVPDdkkj .icon-shape p,#mermaid-svg-AR1uvkLbQVPDdkkj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AR1uvkLbQVPDdkkj .icon-shape .label rect,#mermaid-svg-AR1uvkLbQVPDdkkj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AR1uvkLbQVPDdkkj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AR1uvkLbQVPDdkkj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AR1uvkLbQVPDdkkj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 没有



逐日上涨不回落
突发尖峰
进程突然消失,无日志
journalctl -k

搜 Out of memory / Killed process
有 OOM kill 记录?
不是 OOM

转 §3.1 崩溃 / §3.5 启动失败
含 Memory cgroup

out of memory?
打到服务自身 cgroup 限额

systemctl show 查 MemoryMax
整机内存不足

记录被杀进程 anon-rss
RSS 增长形态?
慢性泄漏

pmap / heap dump 定位增长段
突发大分配

大查询 / 批量任务 / 缓存击穿

排查命令

第一步:确认是 OOM、确认杀的是谁。

bash 复制代码
journalctl -k | grep -iE 'oom|killed process' | tail -20

看什么Out of memory: Killed process <pid> (<名字>) total-vm:xxxkB, anon-rss:xxxkB ------被杀进程实际占用的物理内存(anon-rss)是多少。

bash 复制代码
journalctl -k | grep -iB 20 'killed process' | grep -i 'memory cgroup out of memory'

看什么 :是否出现 Memory cgroup out of memory------有,说明是该服务自己的 cgroup 内存上限被打爆(unit 里配了 MemoryMax=),不是整机内存不足,排查范围直接缩小到这一个服务。

第二步:当前内存画像。

bash 复制代码
free -h

看什么 :盯 available(真正可用),不是 freebuff/cache 高通常是正常文件缓存,可回收,不算泄漏。

bash 复制代码
grep -E 'MemAvailable|Slab|SReclaimable|PageTables|KernelStack' /proc/meminfo

看什么Slab 是否异常大(内核对象泄漏);PageTables 是否异常大(进程虚拟地址空间过大,常见于配置过大的 JVM 堆)。

bash 复制代码
ps aux --sort=-rss | head -15

看什么:RSS 第一名是谁,占整机内存的比例。

bash 复制代码
systemd-cgtop -m

看什么:按 cgroup(≈按服务)排序的内存占用榜,找出哪个单元吃得最多。

第三步:追踪泄漏。

bash 复制代码
grep -E 'VmRSS|VmHWM|RssAnon|Threads' /proc/<PID>/status

看什么 :每隔几分钟采样一次 VmRSS------只涨不落 = 疑似泄漏;VmHWM 是历史峰值。

bash 复制代码
pmap -x <PID> | tail -1

看什么:进程 RSS 总量;间隔运行两次做差,看增量在哪个映射段(配合下条命令)。

bash 复制代码
pmap -x <PID> | sort -k3 -n -r | head -15

看什么 :RSS 最大的匿名映射段------[heap] 持续增长 = 应用内存泄漏;出现大量 64MB 左右的匿名段增长 = glibc malloc arena 碎片化,可试 MALLOC_ARENA_MAX=2

bash 复制代码
slabtop -o | head -15

看什么 :整机 SLAB 大时,哪类内核对象最多------dentry_cache / inode_cache 暴涨通常意味着某个进程在遍历/创建海量小文件。

判断依据

证据 结论
被杀进程 anon-rss ≈ 整机内存 进程真吃得多:泄漏或容量规划错误
日志含 Memory cgroup out of memory 是 systemd 限额被打爆,查 `systemctl show
RSS 缓慢爬升数天后被杀 典型慢性泄漏 → 应用层 heap dump / pmap 定位
内存尖峰后立刻被杀 突发大对象:大查询、大批量任务、缓存击穿
free 不高但仍 OOM 查 cgroup 限制;或 PageTables/SLAB 等不可回收部分占比

缓解手段(不是根治,买时间用):

bash 复制代码
echo -500 > /proc/<PID>/oom_score_adj

看什么 :oom_score_adj 范围 -1000~1000,越小越不容易被杀;systemd 服务请在 unit 里配 OOMScoreAdjust=,重启不丢失。


3.4 性能劣化

现象

  • 接口 P99 延迟上涨、吞吐下降
  • 周期性变慢(每天固定时间、每周某天)
  • 用户反馈"比以前卡",但没报错

工具uptimevmstatiostatpidstatperftop -H

排查流程图
#mermaid-svg-49SYvazk7NFf7Sw6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-49SYvazk7NFf7Sw6 .error-icon{fill:#552222;}#mermaid-svg-49SYvazk7NFf7Sw6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-49SYvazk7NFf7Sw6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-49SYvazk7NFf7Sw6 .marker.cross{stroke:#333333;}#mermaid-svg-49SYvazk7NFf7Sw6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-49SYvazk7NFf7Sw6 p{margin:0;}#mermaid-svg-49SYvazk7NFf7Sw6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster-label text{fill:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster-label span{color:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster-label span p{background-color:transparent;}#mermaid-svg-49SYvazk7NFf7Sw6 .label text,#mermaid-svg-49SYvazk7NFf7Sw6 span{fill:#333;color:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 .node rect,#mermaid-svg-49SYvazk7NFf7Sw6 .node circle,#mermaid-svg-49SYvazk7NFf7Sw6 .node ellipse,#mermaid-svg-49SYvazk7NFf7Sw6 .node polygon,#mermaid-svg-49SYvazk7NFf7Sw6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-49SYvazk7NFf7Sw6 .rough-node .label text,#mermaid-svg-49SYvazk7NFf7Sw6 .node .label text,#mermaid-svg-49SYvazk7NFf7Sw6 .image-shape .label,#mermaid-svg-49SYvazk7NFf7Sw6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-49SYvazk7NFf7Sw6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-49SYvazk7NFf7Sw6 .rough-node .label,#mermaid-svg-49SYvazk7NFf7Sw6 .node .label,#mermaid-svg-49SYvazk7NFf7Sw6 .image-shape .label,#mermaid-svg-49SYvazk7NFf7Sw6 .icon-shape .label{text-align:center;}#mermaid-svg-49SYvazk7NFf7Sw6 .node.clickable{cursor:pointer;}#mermaid-svg-49SYvazk7NFf7Sw6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-49SYvazk7NFf7Sw6 .arrowheadPath{fill:#333333;}#mermaid-svg-49SYvazk7NFf7Sw6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-49SYvazk7NFf7Sw6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-49SYvazk7NFf7Sw6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-49SYvazk7NFf7Sw6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-49SYvazk7NFf7Sw6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-49SYvazk7NFf7Sw6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster text{fill:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster span{color:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-49SYvazk7NFf7Sw6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-49SYvazk7NFf7Sw6 .icon-shape,#mermaid-svg-49SYvazk7NFf7Sw6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-49SYvazk7NFf7Sw6 .icon-shape p,#mermaid-svg-49SYvazk7NFf7Sw6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-49SYvazk7NFf7Sw6 .icon-shape .label rect,#mermaid-svg-49SYvazk7NFf7Sw6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-49SYvazk7NFf7Sw6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-49SYvazk7NFf7Sw6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-49SYvazk7NFf7Sw6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} r 大于核数,us 高
sy 高,cs 暴涨
wa 高,b 列大于 0
si/so 非 0
st 高
指标全正常
延迟升高 / 吞吐下降
vmstat 1 象限定界
哪个指标异常?
用户态吃 CPU

pidstat -u 找进程
perf top / perf record -g

定位热点函数
内核态 / 上下文切换风暴

pidstat -w,查线程数与锁
IO 瓶颈

iostat -xz 找饱和盘
pidstat -d 找进程

查慢 SQL / 日志狂写 / 盘老化
正在换页

转 §3.3 内存排查
宿主机偷 CPU

云 VM 邻居噪音:迁移 / 升配
非资源瓶颈

锁竞争 / 下游依赖慢 / 网络

排查命令

第一步:定位瓶颈象限。

bash 复制代码
uptime

看什么:load 三个数(1/5/15 分钟)的相对大小------1 分钟远高于 15 分钟 = 正在恶化;反之路过高峰在恢复。

bash 复制代码
vmstat 1 10

看什么

  • r(运行队列)持续 > CPU 核数 → CPU 不够
  • b(阻塞进程)持续 > 0 → 在等 IO
  • us 高 → 用户态(应用代码)耗 CPU;sy 高 → 内核态(系统调用/上下文切换)耗 CPU
  • wa 高 → CPU 空转等 IO;st 高 → 宿主机偷走了 CPU(云虚拟机常见)
  • si/so 非 0 → 正在换页,内存已捉襟见肘
  • cs(上下文切换)暴涨 → 线程过多或锁竞争激烈

第二步:磁盘维度。

bash 复制代码
iostat -xz 1 5

看什么%util 接近 100 = 该盘已饱和;await 明显高于平时(SSD 正常 <1ms,HDD <15ms)= 单次 IO 慢;aqu-sz 大 = 请求在排队。

第三步:定位到进程。

bash 复制代码
pidstat -u 1 5

看什么 :哪个进程的 %CPU 最高。

bash 复制代码
pidstat -d 1 5

看什么 :哪个进程的 kB_wr/skB_rd/s 最大------把 §第一步的 IO 饱和和具体进程对上号。

bash 复制代码
pidstat -w 1 5

看什么nvcsw/s(主动切换)高 = 频繁等锁/等 IO;csw/s(被动切换)高 = CPU 争抢,时间片被强制让出。

bash 复制代码
top -H -p <PID>

看什么:CPU 消耗集中在哪个线程,记下 TID,供 perf / gdb 精确定位。

第四步:CPU 热点函数。

bash 复制代码
perf top

看什么 :实时函数热点榜------业务函数占比高就是算法问题;__libc_malloc__GI___libc_free 占比高提示频繁分配;内核函数(如 _raw_spin_lock)占比高提示内核态竞争。

bash 复制代码
perf record -F 99 -g -p <PID> -- sleep 30 && perf report

看什么 :30 秒采样后的调用树,占比最高的调用链就是优化对象。若报权限错误:sudo sysctl kernel.perf_event_paranoid=1;虚拟机里若不可用,退回 pidstat + top -H 组合。

判断依据

观测组合 瓶颈判定 下一步
us 高 + r 应用吃 CPU pidstat 找进程 → perf 找函数
sy 高 + cs 上下文切换 / 系统调用风暴 pidstat -w 找进程;查线程数是否失控
wa 高 + b>0 + %util≈100 磁盘 IO 饱和 iostat 定盘 → pidstat -d 定进程 → 查慢 SQL/日志狂写
st 宿主机超卖 云 VM "邻居噪音",考虑迁移/升配
si/so 非 0 内存不足引发换页 转 §3.3 内存排查
各项资源都不高但延迟高 锁竞争 / 外部依赖慢 strace 看 futex;查下游服务与网络

3.5 服务启动失败

现象

  • systemctl start <svc> 直接报错,或状态在 activating(auto-restart) 循环
  • systemctl status 显示 code=exited, status=N

工具systemctljournalctlsystemd-analyzestrace

排查流程图
#mermaid-svg-Jrcs4mnyRpIcbgXX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Jrcs4mnyRpIcbgXX .error-icon{fill:#552222;}#mermaid-svg-Jrcs4mnyRpIcbgXX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Jrcs4mnyRpIcbgXX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .marker.cross{stroke:#333333;}#mermaid-svg-Jrcs4mnyRpIcbgXX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Jrcs4mnyRpIcbgXX p{margin:0;}#mermaid-svg-Jrcs4mnyRpIcbgXX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster-label text{fill:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster-label span{color:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster-label span p{background-color:transparent;}#mermaid-svg-Jrcs4mnyRpIcbgXX .label text,#mermaid-svg-Jrcs4mnyRpIcbgXX span{fill:#333;color:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .node rect,#mermaid-svg-Jrcs4mnyRpIcbgXX .node circle,#mermaid-svg-Jrcs4mnyRpIcbgXX .node ellipse,#mermaid-svg-Jrcs4mnyRpIcbgXX .node polygon,#mermaid-svg-Jrcs4mnyRpIcbgXX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .rough-node .label text,#mermaid-svg-Jrcs4mnyRpIcbgXX .node .label text,#mermaid-svg-Jrcs4mnyRpIcbgXX .image-shape .label,#mermaid-svg-Jrcs4mnyRpIcbgXX .icon-shape .label{text-anchor:middle;}#mermaid-svg-Jrcs4mnyRpIcbgXX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .rough-node .label,#mermaid-svg-Jrcs4mnyRpIcbgXX .node .label,#mermaid-svg-Jrcs4mnyRpIcbgXX .image-shape .label,#mermaid-svg-Jrcs4mnyRpIcbgXX .icon-shape .label{text-align:center;}#mermaid-svg-Jrcs4mnyRpIcbgXX .node.clickable{cursor:pointer;}#mermaid-svg-Jrcs4mnyRpIcbgXX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .arrowheadPath{fill:#333333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Jrcs4mnyRpIcbgXX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Jrcs4mnyRpIcbgXX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster text{fill:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster span{color:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Jrcs4mnyRpIcbgXX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX rect.text{fill:none;stroke-width:0;}#mermaid-svg-Jrcs4mnyRpIcbgXX .icon-shape,#mermaid-svg-Jrcs4mnyRpIcbgXX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Jrcs4mnyRpIcbgXX .icon-shape p,#mermaid-svg-Jrcs4mnyRpIcbgXX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .icon-shape .label rect,#mermaid-svg-Jrcs4mnyRpIcbgXX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Jrcs4mnyRpIcbgXX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Jrcs4mnyRpIcbgXX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Jrcs4mnyRpIcbgXX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1 / 2
126 / 127
200 / 203
216 / 217
systemctl start 失败 /

auto-restart 循环
systemctl status -l

记退出码与日志末尾几行
退出码归类?
应用自身报错

journalctl -u -o cat 看栈
无执行权限 / 找不到命令

查 ExecStart 路径与权限
WorkingDirectory / 可执行文件问题

systemd-analyze verify
User / Group 不存在

修正 unit 文件
五类常见根因逐一排查
端口冲突

ss -lntp 看谁占用
配置错误

手动运行验证
权限不足

sudo -u 复现
文件缺失

strace openat 看 ENOENT
依赖未就绪

查 After= 顺序

排查命令

bash 复制代码
systemctl status <svc> -l --no-pager

看什么 :三件事------① Active 状态;② Main PID ... code=exited, status=N 退出码;③ 末尾几行日志,往往直接写着原因。

bash 复制代码
journalctl -u <svc> -b --no-pager | tail -100

看什么 :本次开机该服务的全部日志------配置解析错误(parse errorinvalid)、端口冲突(Address already in use)、权限拒绝(Permission denied)、依赖连不上(Connection refused)。

bash 复制代码
journalctl -u <svc> -o cat

看什么-o cat 去掉 systemd 前缀,只留应用原始输出,业务异常栈更容易看出来。

退出码速查

status 含义 排查方向
1 / 2 应用自身报错退出 看应用日志
126 文件存在但不可执行 文件权限(chmod +x
127 命令或依赖文件找不到 ExecStart 路径、动态库缺失
200 (CHDIR) 工作目录不可进入 WorkingDirectory= 指向的目录不存在
203 (EXEC) 无法执行 ExecStart 路径写错、无权限、脚本 shebang 解释器缺失
216 / 217 Group / User 不存在 unit 里 User=Group= 写错

单元文件检查:

bash 复制代码
systemd-analyze verify /etc/systemd/system/<svc>.service

看什么:unit 语法错误和告警,例如 ExecStart 路径不存在。

bash 复制代码
systemctl cat <svc>

看什么 :完整生效配置(含 drop-in 覆盖):UserWorkingDirectoryExecStartEnvironmentFileLimitNOFILE

bash 复制代码
cat $(systemctl show <svc> -p EnvironmentFile --value)

看什么 :环境文件是否存在、格式是否为 KEY=VALUE(不能带 export、不能有行内注释展开)。

端口占用:

bash 复制代码
ss -lntp | grep <端口>

看什么:端口被哪个进程占着------常见于旧进程没退干净,或两个服务配了同一端口。

以运行用户身份手动复现:

bash 复制代码
sudo -u <服务运行用户> <ExecStart 原始命令>

看什么:报什么错。root 手动跑能起来、服务用户跑不起来 = 权限问题;这一步能复现 80% 的启动失败。

日志不足时追踪系统调用:

bash 复制代码
strace -f -e trace=openat,connect,bind -o /tmp/<svc>.trace <ExecStart 原始命令>

看什么 :哪个文件打开失败(= -1 ENOENT 路径不存在 / EACCES 无权限)、哪个 connect/bind 失败。

判断依据:退出码定大方向,日志定具体行。90% 的启动失败归为五类------配置错误、端口冲突、权限不足、路径/文件缺失、依赖服务未就绪。改过 unit 文件后务必:

bash 复制代码
systemctl daemon-reload && systemctl restart <svc>

看什么:不先 daemon-reload,重启用的还是旧配置,会造成"改了没生效"的错觉。


4. 常用工具速查表

标注「自带」的为 Ubuntu 服务器默认安装;其余一条命令装齐:

bash 复制代码
sudo apt update && sudo apt install -y gdb strace lsof sysstat iotop tcpdump
sudo apt install -y linux-tools-common linux-tools-generic "linux-tools-$(uname -r)"

看什么 :第二条装 perf,必须和当前内核版本匹配,装完用 perf --version 验证能运行。

工具 来源 典型场景 常用命令示例 重点看什么
top 自带 (procps) 总览 CPU / load / 热点线程 toptop -H -p <PID> load、us/sy/wa、哪个线程吃 CPU
ps 自带 (procps) 进程快照与进程树 ps auxf、`ps -eLf wc -l`
vmstat 自带 (procps) CPU/内存/切换趋势 vmstat 1 r/b、us/sy/wa/st、si/so、cs
iostat sysstat 磁盘饱和度 iostat -xz 1 %util、await、aqu-sz
pidstat sysstat 按进程定位 CPU/IO/切换 pidstat -u 1 / -d 1 / -w 1 哪个进程在耗资源
sar sysstat 回看历史指标 sar -u -f /var/log/sysstat/sa15 故障发生时刻的指标
perf linux-tools CPU 热点与调用栈 perf topperf record -g 热点函数、调用链占比
strace 需安装 系统调用跟踪 strace -p <PID> -tt -T -f 卡在哪个调用、错误码(ENOENT/EACCES)
gdb 需安装 调用栈 / 死锁 / core 分析 gdb -p <PID>thread apply all bt 线程栈、崩溃 backtrace
lsof 需安装 文件 / 连接 / 端口占用 lsof -p <PID>lsof -i :80 fd 数量与类型、谁占着端口
ss 自带 (iproute2) 网络连接状态 ss -lntpss -sss -tnp 监听是否齐全、状态分布、队列积压
dmesg 自带 内核消息 `dmesg -T taildmesg -w`
journalctl 自带 (systemd) 系统与服务的统一日志 journalctl -u <svc> -bjournalctl -k 错误前后文、时间对齐
coredumpctl 自带 (systemd) core dump 管理 coredumpctl listcoredumpctl gdb <名> 有无 core、直接加载分析
pmap 自带 (procps) 进程内存映射 pmap -x <PID> 哪段映射的 RSS 在涨
slabtop 自带 (procps) 内核 SLAB 缓存 slabtop -o 哪类内核对象占内存
systemd-cgtop 自带 (systemd) 按服务看资源占用 systemd-cgtop -m 各 cgroup 内存/CPU 排行
systemd-analyze 自带 (systemd) 开机与 unit 诊断 systemd-analyze blameverify 开机慢在哪、unit 语法
iotop 需安装 实时看谁在读写磁盘 iotop -oP IO 进程排行(需要 root)
tcpdump 需安装 抓包 tcpdump -i any port 80 -w /tmp/x.pcap 握手是否完成、重传、RST

工具使用分寸

  • strace -p 让目标进程明显变慢,生产环境只做短时间观察。
  • gdb -p attach 期间进程被冻结,对延迟敏感的服务优先走 core dump 离线分析。
  • perf record 采样开销低(1% 以内),生产可用,但查完删除 perf.data。

5. 三个真实案例复盘

案例一:凌晨随机崩溃,应用日志干干净净(崩溃类)

现象:某图片处理服务(C++ 核心 + 上层封装)凌晨 03:12 被监控发现连续重启 2 次,期间部分任务处理失败。翻遍应用日志只有正常业务输出,没有任何异常栈。

排查过程

bash 复制代码
systemctl status imgsvc

看什么Main process exited, code=killed, status=11/SEGV ------进程不是自己退的,是被 SIGSEGV 杀掉的,应用日志里没有栈很正常(段错误不走异常处理)。

bash 复制代码
journalctl -k -b | grep -i segfault

看什么segfault at 18 ip 00007f2c... error 4 in libjpeg.so.8.2.2 ------崩溃点在 libjpeg 内部,不是业务代码直接崩。

bash 复制代码
coredumpctl list imgsvc

看什么:03:12 有 core 记录,可离线分析。

bash 复制代码
coredumpctl gdb imgsvc
gdb 复制代码
bt

看什么 :栈顶在 libjpeg 的 decompress_smooth_data,上一帧是业务的图片解码入口------解码某张特定图片时崩溃。

根因 :上游偶发传入截断的 JPEG 文件;所用 libjpeg 旧版本对这类畸形文件有已知崩溃缺陷。

处理 :升级 libjpeg;业务侧在解码前做文件完整性校验,畸形文件直接拒绝并告警。

复盘要点 :应用日志没有错误 ≠ 应用没出错------信号类崩溃不会走日志路径;dmesgcoredumpctl 才是第一现场;native 库的坑通常要特定输入才触发,复现靠留下问题文件。


案例二:超时风暴,但 CPU 很空闲(挂起 / 性能类)

现象:某工作日下午 API 网关大量超时,监控显示 CPU 利用率仅 15% 左右,但 load 从 4 一路涨到 30+。典型的"机器不忙但就是卡"。

排查过程

bash 复制代码
vmstat 1

看什么r=2 b=41 ... wa=46 ------运行队列很短,但有 41 个进程堵在不可中断睡眠,CPU 近一半时间在等 IO。CPU 空闲 + b 列大 = 瓶颈在磁盘。

bash 复制代码
iostat -xz 1

看什么sda %util=100 await=860 ------唯一的数据盘已完全饱和,单次 IO 平均等 860ms。

bash 复制代码
pidstat -d 1

看什么postgres kB_wr/s=41200 ------数据库进程在疯狂写盘。但 PostgreSQL 平时写入只有几百 kB/s。

bash 复制代码
ss -tn | grep :5432 | wc -l

看什么:数据库连接数从平时 80 涨到 240------请求处理被 IO 拖慢,连接池被占满,新请求排队超时。

继续查"谁让数据库写这么多":慢查询日志无异常,最后定位到当天新上线的一个数据导出任务在跑全表扫描 + 大批量写临时文件,和业务库共用同一块盘。

根因 :导出任务与数据库争抢同一块磁盘的 IO,数据库写入被拖垮,连接堆积引发级联超时。

处理 :立即停止导出任务,服务分钟级恢复;后续把批量任务挪到低峰时段、用 ionice -c3 降为最低 IO 优先级、备份/导出使用独立磁盘。

复盘要点 :"CPU 空闲 ≠ 系统空闲",vmstat 的 b 列和 wa 是识破假象的第一把钥匙;IO 饱和时 load 会涨但 CPU 不涨;批量任务上线必须有资源限制和时间窗口评审。


案例三:每 2~4 天被杀一次(OOM 类)

现象:某 Java 服务运行 2~4 天后进程消失,systemd 自动拉起,如此循环。业务侧感知为"每隔几天有一次几分钟的服务抖动"。

排查过程

bash 复制代码
journalctl -k | grep -i 'killed process'

看什么Out of memory: Killed process 2451 (java) total-vm:8388608kB, anon-rss:7912448kB ------实锤 OOM;anon-rss 约 7.9GB,而 JVM 堆只配了 4GB,说明堆外也有大量增长,或堆增长不受 -Xmx 约束的部分在吃内存。

bash 复制代码
free -h

看什么 :事故前 available 只剩 200MB 左右,整机 8GB 内存。

追踪趋势(连续观察两天):

bash 复制代码
grep VmRSS /proc/<PID>/status

看什么:RSS 从 4.2GB 单调爬升到 7.9GB,中间没有回落------"只涨不落"就是泄漏的典型曲线,与突发大对象(尖峰)形态不同。

bash 复制代码
pmap -x <PID> | sort -k3 -n -r | head

看什么:增长集中在 heap 段,指向 JVM 堆内对象泄漏,而不是 native 泄漏。

应用侧:jmap -dump 导出堆快照分析,发现一个本地会话缓存(Map<String, Session>)条目超过 200 万,只进不出。

根因 :会话缓存只添加不清理、无过期机制,堆持续增长直至整机 OOM,内核 OOM killer 选中 RSS 最大的 java 进程。

处理 :改用带容量上限 + 过期淘汰的缓存组件;同时在监控里加了该服务 RSS 的告警(阈值 6GB),保证下次泄漏在 OOM 之前被发现。

复盘要点 :OOM 排查第一步永远是 journalctl -k 确认"杀的是谁、用了多少";"周期性被杀 + RSS 缓慢爬升"是内存泄漏的教科书形态;修复泄漏之外,必须补上 RSS 监控告警,否则下次还是靠用户反馈发现。


6. 排查记录模板

每次真实排查都留一份记录,既是复盘材料,也是下次遇到同类问题的检索入口。

markdown 复制代码
# 排查记录:<一句话标题,例:订单服务凌晨 OOM 重启>

## 基本信息
| 项目 | 内容 |
|------|------|
| 发生时间 | YYYY-MM-DD HH:MM |
| 发现方式 | 告警 / 用户反馈 / 巡检 |
| 影响服务 | |
| 影响范围 | |
| 严重等级 | P0-P3 |
| 处理人 | |

## 1. 现象描述
<监控上看到了什么、用户感知是什么、从何时开始、是否可复现>

## 2. 时间线
| 时间 | 事件 |
|------|------|
| HH:MM | 告警触发 / 现象出现 |
| HH:MM | 首次响应,初步判断方向 |
| HH:MM | 定位到根因 |
| HH:MM | 止血 / 恢复 |

## 3. 信息采集记录
| 命令 | 目的 | 关键输出 / 结论 |
|------|------|------------------|
| free -h | 看内存 | available 仅剩 200MB |
| | | |

## 4. 排查过程(假设 → 验证)
1. 假设 A:<...> → 用 <命令> 验证 → 排除 / 确认
2. 假设 B:<...> → 用 <命令> 验证 → 排除 / 确认

## 5. 根因
<一句话说清根因 + 机制解释>

## 6. 处置与修复
<紧急止血措施 + 长期修复方案,关联 commit / 配置变更记录>

## 7. 后续行动项
| 事项 | 负责人 | 截止时间 |
|------|--------|----------|
| 补充监控告警 | | |
| | | |

## 8. 附录
- 相关日志 / 截图 / core 文件路径:
- 相关链接(监控面板、工单、PR):

附录:常见误区与注意事项

  1. 不要上来就重启。重启会销毁现场(内存数据、连接状态、core 机会);除非影响面正在扩大,先跑完 §1 采集清单。
  2. free 里 buff/cache 高不是内存泄漏 ,判断标准是 available
  3. load 高 ≠ CPU 高。D 状态(不可中断睡眠)进程同样计入 load,磁盘卡住时 load 可以非常高而 CPU 很闲。
  4. strace 有显著开销,生产环境 attach 只做短时观察,用完立即退出。
  5. gdb attach 会冻结进程,对延迟敏感的服务优先离线分析 core dump。
  6. perf 必须匹配内核版本linux-tools-$(uname -r);虚拟机报权限错误时调低 kernel.perf_event_paranoid
  7. Ubuntu 的 core dump 被 systemd-coredump 接管 ,用 coredumpctl 查,不要去进程目录里找 core 文件。
  8. 区分两种退出code=killed 是被信号杀(外部/OOM/段错误),code=exited 是进程自己退出(看 status 码)。
  9. 磁盘"满"有两种 :容量满(df -h)和 inode 满(df -i),后者更隐蔽。
  10. 改了 unit 文件先 systemctl daemon-reload,否则重启加载的还是旧配置。
相关推荐
juesdo1 小时前
vulnos OS-00118靶场通关
linux·web安全·网络安全·docker
霸刀1 小时前
nslookup 与 dig 使用指南:DNS 查询、区别对比及真实案例解读
linux·cdn·dns
郝亚军1 小时前
asqlite-autoconf-3310100 的ubuntu 22.04 aarch64交叉编译
linux·运维·ubuntu
笑锝没心没肺1 小时前
Linux安装docker及运行容器测试
linux·运维·docker
Mr_liu_6662 小时前
Claude非专业入门实战笔记(附录):关闭Telemetry-遥测,消除无用网络请求与日志噪音
linux·笔记·ubuntu·ai编程
时空无限2 小时前
ubuntu apt 查看软件之间的依赖
linux·运维·ubuntu
Escalating_xu2 小时前
【Linux】线程概念与控制:从地址空间、LWP 到 pthread_create、join、detach 与 NPTL
java·linux·redis
Escalating_xu2 小时前
【Linux】进程信号:从产生、阻塞与递达到 sigaction、可重入与内核返回路径
linux·运维·面试·职场和发展
2603_966437262 小时前
格式化之后数据还能恢复吗?说清恢复条件
linux·运维·数据库