LabHub
学习 学习路径 课程

CPU 与内存泄漏的判定

load average 不是 CPU 使用率

在 LabHub 中继续学习

一句话总结

Linux 的 load average 是可运行进程数加上处于不可中断等待(D 状态)的进程数。磁盘变慢时它也会上升;它衡量的并不是 CPU 使用率。

概念图: 可运行进程数加上处于不可中断等待(D 状态)的进程数 · 系统需求指标,而不是 CPU 指标 · top 中的 %CPU 超过 100 并不是缺陷。 · 单个核心为基准

为什么需要这些知识

“load 达到 20,应该增加 CPU”是一种常见误诊。在 8 核机器上,load 20 可能表示 CPU 不足,也可能表示 NFS 停滞,20 个进程都被困在 I/O 等待中。面对后一种情况,增加 CPU 不会产生任何效果。

其他 Unix 系统计算 load 时只统计等待 CPU 的进程,只有 Linux 还会统计 D 状态(uninterruptible sleep)。这是 1993 年为了更准确表示“系统繁忙”而引入的变更。从那以后,Linux load 就成为系统需求指标,而不是 CPU 指标

它是如何运作的

三个数字分别是 1 分钟、5 分钟和 15 分钟的指数移动平均值。变化方向本身就是信息。

cat /proc/loadavg
# 8.24 4.10 2.05 3/512 12345
#  └1분 └5분 └15분 └실행중/전체 스레드 └마지막 PID

1 分钟值大于 15 分钟值,说明情况正在恶化;反之则表示正在恢复。

要确定是什么推高了 load,应按状态统计。

ps -eo state,comm --no-headers | awk '{print $1}' | sort | uniq -c | sort -rn
# R = 실행 가능, D = 끊기지 않는 대기(대개 I/O), S = 잠듦

如果 D 很多,问题就不在 CPU。

要查看 CPU 本身,应读取两次 /proc/stat 并计算差值。

awk '/^cpu /{print $2+$3+$4+$6+$7+$8, $5}' /proc/stat   # (busy, idle)

使用两个时点的差值计算使用率,这正是 top 所做的事情。

常见误解

top 中的 %CPU 超过 100 并不是缺陷。 默认模式下,%CPU 以单个核心为基准。4 个线程各自满负载运行时,结果就是 400%。在 top 中按 Shift+I,即可切换为以全部核心为基准的模式,即关闭 Irix 模式。

容器内的 top 查看的是整个宿主机。 因为 /proc 来自宿主机,所以核心数和 load 都是宿主机的值。容器实际分配到的份额必须通过 cgroup 查看。

cat /sys/fs/cgroup/cpu.max        # "쿼터 주기" — max 면 제한 없음
cat /sys/fs/cgroup/cpu.stat       # nr_throttled, throttled_usec

如果 nr_throttled 持续增加,说明不是 CPU 不足,而是触及了限制。二者的处理方式完全不同。

负载平均值不是 CPU 指标

Linux 的 load average 表示正在运行或等待运行的进程数量,但其中还包括 D 状态,即不可中断等待,通常是磁盘 I/O。这一点不同于其他 Unix,也是误解的来源。

$ uptime
 load average: 8.42, 6.10, 4.33     ← 코어가 4개인데 8?
$ vmstat 1 3
 r  b   ...     ← r 은 실행 대기, b 는 I/O 대기
 1  7   ...     ← 실행 대기는 1뿐, 나머지 7은 디스크를 기다린다

如果 r 小而 b 大,增加 CPU 不会带来任何改善。 真正的问题在磁盘。仅凭 load average 就扩大实例规模,是对这个指标代价最高的误解。

使用率相同,原因也可能不同

top 中哪一列数值较大,决定了调查方向。

较大的列 含义 首先查看的位置
us (user) 应用程序代码 profiler、热点函数
sy (system) 内核——系统调用过多 strace -c、文件与 socket 使用模式
wa (iowait) 等待磁盘 iostat -xawait
si (softirq) 网络中断 /proc/softirqs、RSS/RPS 设置
st (steal) 被 hypervisor 抢走的时间 云实例等级、邻居噪声问题

如果 st 超过 5%,问题就不在自己的代码。可能是可突发实例(t 系列)耗尽了信用额度,也可能是物理宿主机过度拥挤。无论如何修改代码,都无法解决。

查找谁在使用 CPU 的顺序

  1. 谁在使用——通过 top -H -p <pid> 深入到线程级别。很多时候并不是整个进程都在运行,而是某一个线程持续占用,例如 GC 线程或轮询循环。
  2. 在哪里使用——通过 perf top -p <pid> 查看热点符号。如果大量符号显示为 [unknown],说明缺少调试符号,应改用对应语言的 profiler。
  3. 为什么使用——回到代码中查看该函数为何如此频繁地被调用。通常是 O(n²)、缓存未命中,或同步写入日志。

没有 perf 的容器中,也可以通过 /proc/<pid>/stack 或语言运行时的线程 dump(jstackpy-spy dump)了解一半情况。

生产环境中真正重要的事

在 Kubernetes 中设置 CPU limit,会启用 cgroup quota。请求涌入时发生 throttling,p99 延迟会突然升高,但 CPU 使用率图表会在 limit 附近保持平坦,很容易被误读为“仍有余量”。确认 throttling 应查看 cpu.stat,而不是使用率。