云服务器CPU不高但响应慢,如何结合可用内存、交换空间和进程占用定位瓶颈?

云服务器 CPU 使用率不高但响应慢,可能是哪些资源出现瓶颈?从内存角度排查,重点看业务变慢时的可用内存、缓存回收、交换活动、进程占用和 OOM 事件,而不是只看 CPU 百分比或 free 一列。若 MemAvailable 持续下降,同时 vmstat 的 si、so 持续有值,且这些变化与请求延迟、阻塞任务或内存压力指标升高的时间相吻合,内存压力或交换读写就值得优先排查。
先在业务变慢时采集一组低风险信息:
free -h
vmstat 1 10
swapon --show
单独看到 free 很低、缓存较大或 Swap 已使用,都不能直接认定内存是瓶颈。判断需要结合一段时间内的变化,并进一步确认是系统整体内存紧张、某个进程占用增长,还是容器或服务的内存上限触发。
先判断内存压力是否与响应变慢同时发生
在常见 Linux 系统中,free -h 会显示 total、used、free、buff/cache 和 available 等字段。排查时优先关注 available,而不是只看 free:
free是当前未使用的物理内存。buff/cache是缓冲区和文件缓存等占用。Linux 会利用空闲内存缓存文件,缓存通常可在需要时回收,因此它较大本身不代表内存不足。available是系统预计可供新程序使用的内存,比单看free更适合判断当前是否有内存余量。Swap used表示已有部分内存页进入交换空间,不表示这些页面此刻正在频繁读写。
如果业务变慢时 MemAvailable 仍稳定,交换活动也不明显,不能因为 free 较低或 buff/cache 较大就判定内存不足。反过来,如果 MemAvailable 在慢请求期间持续下降,并伴随交换活动、进程阻塞或 OOM 事件,内存压力影响业务的可能性就更高。
buff/cache 也不等于可以瞬间全部释放的内存,其中包含不同类型的缓存和内核占用。不要把 free + buff/cache 简单当作完全可用内存,也不建议为了“腾内存”直接清理缓存。
区分“曾经用过 Swap”和“当前正在交换”
可以用以下命令查看交换空间:
swapon --show
cat /proc/swaps
交换空间要分别看已使用量和当前活动。已使用量只能说明部分页面曾被换出;要判断现在是否正在频繁交换,还要观察 vmstat 的 si、so:
vmstat 1 10
重点字段如下:
| 字段 | 含义 | 排查时如何理解 |
|---|---|---|
si | 从交换空间换入内存的数据量 | 连续多个采样周期有值,说明正在发生换入 |
so | 从内存换出到交换空间的数据量 | 连续有值,可能反映内存回收压力 |
b | 不可中断睡眠任务数量 | 结合交换活动和 wa 判断是否有等待 |
wa | CPU 等待 I/O 的时间占比 | 与 si、so 同时升高时,需关注页面读写等待 |
r | 可运行任务数量 | 持续较高可能存在调度排队,但不能单独证明内存不足 |
free | 未使用内存量 | 仅作辅助,不替代 MemAvailable |
vmstat 的第一行在不少系统上反映启动以来的平均情况,判断当前变化时应重点看后续按间隔采集的行。若 si、so 持续出现非零值,同时 MemAvailable 下降、b 或 wa 升高,而且与请求延迟在时间上重合,才比较符合内存压力导致交换读写、进而拖慢请求的情况。等待页面换入的线程可能处于阻塞状态,不会持续消耗用户态 CPU,因此可能出现 CPU 不高但响应变慢。
若 Swap 已使用,但 si、so 长时间为零,MemAvailable 也稳定,可能只是之前被换出的冷页面尚未再次访问。这种情况不能仅凭 Swap 使用量判定为当前瓶颈。
较新的 Linux 内核还可能提供内存压力信息:
if [ -r /proc/pressure/memory ]; then
cat /proc/pressure/memory
else
echo "当前系统未提供 /proc/pressure/memory"
fi
其中 some 表示至少有部分任务因内存资源而停顿,full 表示更严重的整体停顿。不同系统和负载的正常水平可能不同,不宜套用一个固定阈值;应观察指标是否在业务变慢期间持续升高,并与 MemAvailable、交换活动和请求延迟对照。
从进程占用找出内存压力来源
确认系统有内存压力后,再定位哪些进程占用较多。以下命令适用于常见 Linux 环境,可按常驻内存初筛:
ps -eo pid,ppid,user,%mem,rss,vsz,stat,comm --sort=-rss | head -n 20
RSS是进程当前驻留在物理内存中的大小,通常以 KB 为单位。VSZ是进程可见的虚拟地址空间大小,不能直接当作实际物理内存占用。%MEM表示进程占系统物理内存的比例。STAT中出现D,通常表示进程处于不可中断睡眠;需要结合交换活动和其他等待情况判断原因。
RSS 适合初筛,但多个进程可能共享动态库、共享内存或文件页,直接相加可能高估实际消耗。需要进一步检查某个进程时,可读取其 /proc 信息:
PID=1234
grep -E '^(Name|State|VmPeak|VmSize|VmRSS|RssAnon|RssFile|RssShmem|VmSwap|Threads):' \
/proc/"$PID"/status
if [ -r /proc/"$PID"/smaps_rollup ]; then
grep -E '^(Rss|Pss|Pss_Anon|Pss_File|Pss_Shmem|SwapPss):' \
/proc/"$PID"/smaps_rollup
fi
将 1234 替换为实际进程号。smaps_rollup 并非所有内核都提供,因此命令先检查文件是否可读。
RssAnon较大,通常表示匿名内存占用较多,可能来自堆、运行时对象或进程数据。RssFile较大,可能包含文件映射和文件页,不能据此认定为内存泄漏。VmSwap或SwapPss较大,说明该进程有部分页面进入交换空间,但还需确认当前是否正在交换。Pss会按共享比例分摊共享页面,比 RSS 更适合比较多个进程的实际内存贡献。- 同一负载下 RSS 或 PSS 随时间持续增长,才是进程工作集持续扩大或潜在内存泄漏的线索之一。
建议在业务变慢时和恢复时对同一进程重复采样。若某个进程占用持续增长,且系统 MemAvailable 同步下降,可重点检查该进程的缓存、连接、任务队列、运行时堆和内存释放逻辑。若多个同类进程同时增长,则还要核对进程数量、并发规模及单个进程的固定工作集。
不要仅凭 RSS 最大就终止进程。它可能是核心业务进程,也可能包含较多共享页面。操作前先确认进程归属、服务启动方式、是否存在未落盘状态,以及终止后对请求和数据的影响。
检查 OOM 和服务级内存上限
内存不足时,Linux 可能触发 OOM 处理并终止一个或多个进程。可先查看当前启动周期的内核日志:
journalctl -k -b --no-pager | \
grep -Ei 'out of memory|oom|killed process|memory cgroup'
如果系统未使用 systemd 日志,可尝试:
dmesg -T | grep -Ei 'out of memory|oom|killed process|memory cgroup'
读取 dmesg 可能需要更高权限,部分云主机也会限制普通用户查看内核日志。没有输出不代表一定没有 OOM:日志可能已轮转、权限不足,或者事件发生在上一次启动周期。使用 systemd 的系统可继续检查上一次启动记录:
journalctl -k -b -1 --no-pager | \
grep -Ei 'out of memory|oom|killed process|memory cgroup'
重点核对 OOM 发生时间、被终止进程的名称和 PID、日志中是否出现 memory cgroup 等信息,并确认事件时间是否与响应变慢重合。单条 OOM 日志只能证明曾经发生过内存耗尽,不能单独证明当前每次慢请求都由它导致。
如果业务运行在容器或服务管理器中,宿主机内存看起来充足,也可能是业务所在控制组触及了自己的内存上限。控制组版本和挂载路径会因系统而异。对于确认位于控制组版本 2 根目录的环境,可查看:
for file in memory.current memory.max memory.events; do
if [ -r "/sys/fs/cgroup/$file" ]; then
echo "### $file"
cat "/sys/fs/cgroup/$file"
fi
done
若使用控制组版本 1,可尝试:
for file in memory.usage_in_bytes memory.limit_in_bytes memory.failcnt; do
if [ -r "/sys/fs/cgroup/memory/$file" ]; then
echo "### $file"
cat "/sys/fs/cgroup/memory/$file"
fi
done
在控制组版本 2 中,memory.current 表示当前使用量,memory.max 表示上限,显示为 max 时通常表示未设置固定上限;memory.events 中的 high、oom、oom_kill 可用于判断是否出现过限制或 OOM 事件。
上述路径不一定是所有服务或容器实际所在的控制组目录。若命令没有输出,先检查进程的控制组归属及系统挂载路径,不要据此判断没有限制:
cat /proc/1234/cgroup
将 1234 替换为业务进程 PID。若宿主机 MemAvailable 充足,但业务所在控制组的使用量接近上限,且事件计数增加,应优先核对该服务的内存上限、进程分布和工作集,而不是盲目增加宿主机交换空间。
用时间线区分相似现象
| 观测结果 | 较可能的判断 | 下一步核实 |
|---|---|---|
MemAvailable 下降,si 或 so 持续有值,wa 或 b 同时升高 | 系统内存压力及交换活动可能影响响应 | 对照请求延迟、PSI 和进程 VmSwap |
Swap 已使用,但 si、so 为零,MemAvailable 稳定 | 可能是历史换出页面,当前未必有交换瓶颈 | 在业务慢时继续采样,不仅凭已用量下结论 |
| 宿主机内存充足,但控制组接近上限或事件计数增加 | 服务或容器级内存限制 | 确认进程所属控制组、限制值和事件变化 |
| OOM 日志与响应变慢时间相近 | 曾发生系统级或控制组级内存耗尽 | 核对被终止进程及事件发生时的业务状态 |
| 单个进程 RSS/PSS 持续增长,其他进程变化较小 | 该进程工作集增长、缓存变化或潜在泄漏 | 在相同负载下连续采样 RSS、PSS 和 VmSwap |
MemAvailable、交换活动、PSI 和 OOM 证据均无明显变化 | 现有证据不足以归因于内存 | 不要强行归因,继续按业务现象排查 |
时间关联是重要条件。建议在业务慢的时段连续采集系统指标,并记录对应请求延迟;再对照进程占用、控制组事件和 OOM 日志。指标只有在同一时间段共同变化,才能增强内存瓶颈判断的可信度。
先保留现场,再决定是否调整
排查期间应避免用高风险操作改变现场:
- 不要因为
buff/cache较大就直接清理缓存;清理后文件读取可能重新访问存储,短时间内反而增加延迟。 - 不要只因 Swap 已使用就关闭交换空间;内存峰值时关闭交换可能更快触发 OOM。
- 不要在业务高峰期直接终止最大内存进程;先确认服务、数据和影响范围。
- 不要未经核实就提高或取消控制组内存上限。
- 不要凭一次
free -h输出就扩容或修改内核参数。
如果业务持续不可用,确需重启或停止异常进程,应先保存日志和进程信息,并确认服务启动方式、数据持久化状态、影响范围及维护窗口。操作后通过服务状态、业务健康检查和新的内存采样验证结果;若未恢复,按原启动方式回滚,避免连续叠加未经验证的调整。
最终可执行的判断标准是:只有当业务变慢与内存余量下降或服务级上限触及、交换或内存压力指标变化、进程占用异常或 OOM 事件在时间上相互印证时,才把内存列为主要瓶颈。如果证据只有 CPU 使用率不高或 Swap 已使用,尚不足以归因;应继续采集同一时间段的指标,并确认问题属于系统整体、具体进程还是控制组限制。