Skip to content

Swap 满了但物理内存有剩余

01 前置理论与核心机制​

1. Swap 满了,为什么内存还有剩余?​

先看这台测试虚拟机的结果:

free -h
               total        used        free      shared  buff/cache   available
Mem:           1.9Gi       352Mi       1.5Gi       1.0Mi        86Mi       1.4Gi
Swap:          1.0Gi       1.0Gi          0B

Swap 已经占满,但物理内存还有 1.4 GiB 可用。

原因是:之前内存紧张时,有些数据被换到了 Swap;后来内存空出来了,这些数据却没有马上搬回来。

这里先认识两个值:

  • available:系统估算还能提供给程序使用的内存。排查时主要看这个值。

  • Swap used:现在有多少 Swap 空间被占用。它记录的是占用量,不是读写速度。

2. Linux 为什么不把 Swap 里的数据立即搬回来?​

假设程序 A 占了 1GB 内存,程序 B 又突然申请大量内存。物理内存不够用,Linux 就会把程序 A 暂时不用的部分数据放进 Swap,给程序 B 腾位置。

后来程序 B 退出,物理内存空出来了。但程序 A 还活着,也一直没用到刚才那部分数据,Linux 就继续把它们留在 Swap 中。

等程序 A 再次访问这些数据时,Linux 才把需要的数据读回内存。

所以,Swap 满了,物理内存还有很多剩余,这两个现象并不矛盾。

3. Swap 满了,怎么知道有没有影响业务?​

看两件事:

  • 现在还在不在频繁读写 Swap。 用 vmstat 查看 si、so。

  • 业务有没有变慢。 看接口响应、数据库查询耗时等实际指标。

如果 si、so 基本是 0,说明查看的这几秒里几乎没有从 Swap 读入或写出数据。这是当前没有频繁换页的表现,不代表 Swap 中的数据以后读取时没有开销。

02 故障排查与验证闭环 SOP​

步骤一:查看物理内存和 Swap​

free -h
swapon --show

先看 available 还有多少,再看 Swap 已用了多少。swapon --show 还能告诉你 Swap 用的是文件还是分区。

步骤二:检查当前有没有频繁换页​

vmstat 1 5

重点看:

  • si:每秒从 Swap 读回物理内存的数据量。

  • so:每秒从物理内存写入 Swap 的数据量。

第一行通常是系统启动以来的平均值,重点看后面 4 行实时采样。

这次测试里,后几次 si、so 基本都是 0,说明系统当时没有持续频繁地读写 Swap。

步骤三:查看哪些进程占用了 Swap​

第 07 章提供 check_swap.sh,直接执行:

sudo bash check_swap.sh
这台测试机的输出:
PID        PROCESS                      SWAP(MB)
2446       python3                         974.9
1392       dockerd                           9.6
969        containerd                        5.4
946        networkd-dispat                   4.7
1021       unattended-upgr                   4.3
可以看到,PID 为 2446 的 Python 进程占用了接近 975MB 的 Swap,是主要占用者。

步骤四:查看这个进程的内存详情​

PID=2446
grep -E '^(Name|Pid|VmRSS|VmSwap):' "/proc/$PID/status"
实际输出:
Name:   python3
Pid:    2446
VmRSS:    31532 kB
VmSwap:  998312 kB

  • VmRSS:这个进程现在有多少数据留在物理内存里,约 31MB。

  • VmSwap:这个进程有多少数据放在 Swap 里,约 975MB。

也就是说,这个进程的大部分数据已经被换出,但整台机器的物理内存又空出了 1.4GB。 要解释的现象就在这里。注意一个细节:VmSwap 是进程级统计,统计范围和系统总 Swap 不完全相同,所以把每个进程的 VmSwap 加起来,不一定刚好等于 free -h 的 Swap 使用量。

步骤五:根据排查结果处理​

情况一:Swap 已满,但业务运行正常。available 还有较大余量,vmstat 里 si、so 基本为 0,接口和数据库响应也正常。这说明数据虽然留在 Swap 中,但当前没有持续换页造成的性能问题。保持现状,继续监控即可。情况二:Swap 占用持续增长,业务开始变慢。vmstat 连续多次出现较大的 si、so,说明系统正在反复换入、换出数据。结合 check_swap.sh 查找主要进程,再检查该进程的内存使用、业务负载和服务器容量。找到内存压力来源后,按情况处理应用、调整配置或扩容。不要为了让 Swap 数字归零就直接执行 swapoff -a。 这条命令会停用 Swap,可能需要把大量数据搬回物理内存。内存装不下时,操作可能失败,严重时会触发 OOM,杀掉业务进程。

03 生产故障案例与处理分析(模拟场景)​

案例一:MySQL 服务器 Swap 告警,但数据库没有变慢​

故障现象一台 MySQL 服务器告警,Swap 使用率达到 98%。值班人员查看监控,发现数据库查询耗时正常,物理内存还有 8GB 可用。排查过程先查看内存:

free -h
swapon --show
确认 Swap 已用得差不多,但物理内存还有余量。再观察 10 秒:
vmstat 1 10
后续采样中 si、so 基本为 0,说明当时没有频繁交换内存。接着查占用进程:
sudo bash check_swap.sh
MySQL 进程占用了较多 Swap。再查看历史监控,发现凌晨有一次批量任务,内存使用明显上升;批量任务结束后,物理内存降下来了,但 MySQL 的部分不活跃内存页仍留在 Swap 中。原因和处理这次告警是之前内存紧张留下的 Swap 占用,并不是数据库此刻还在频繁换页。数据库查询正常,所以没有执行 swapoff -a,而是继续观察数据库延迟和换页指标,并检查凌晨批量任务的内存峰值。最终结论:这台服务器的 Swap 告警可以先观察,真正需要优化的是凌晨批量任务带来的内存峰值。

案例二:Java 服务频繁换页,接口从几十毫秒变成几秒​

故障现象一台 Java 应用服务器,接口原本几十毫秒就能返回,后来响应时间上升到数秒。监控同时显示可用物理内存持续下降,Swap 使用量不断增加。排查过程

free -h
vmstat 1 10
这次和案例一不同:available 很低,而且后面多次采样的 si、so 都明显不为 0,说明系统正在频繁交换内存。继续定位进程:
sudo bash check_swap.sh
ps -eo pid,comm,rss --sort=-rss | head
发现 Java 进程占用大量内存,再查看它的启动参数和 JVM 状态:
PID=1234  # 替换为实际 Java 进程 PID
ps -p "$PID" -o pid,etime,rss,vsz,args
jcmd "$PID" VM.flags
jcmd "$PID" GC.heap_info
jcmd 需要服务器安装相应的 JDK 工具,并使用有权限的账号执行。结合内存监控,假设最终查到是 JVM 堆配置偏大,机器还运行了其他服务,几个进程争抢内存,导致频繁换页。原因和处理先把异常实例从负载均衡里摘除,降低业务影响。之后调整 JVM 内存配置,或者为服务增加独立资源。应用恢复后,重新执行 vmstat 1 10,确认换页下降,再检查接口响应时间是否恢复。最终结论:这台服务器的性能问题来自实际内存压力。要处理的是内存配置和资源不足,而不是只清理 Swap。

04 生产排障速查表(Swap 与内存压力)​

排查顺序:先看内存和换页,再找进程,最后结合业务情况决定怎么处理。

05 面试应答参考话术​

问题:Linux Swap 已经满了,但物理内存还有很多剩余,是什么原因?可以这样回答: Linux 之前内存紧张时,会把一些不活跃的内存页换到 Swap。后来其他程序把物理内存释放了,但原来换出去的数据并不会马上全部搬回来,所以会出现 Swap 满了、物理内存还有很多剩余的情况。

排查时,我会先用 free -h 看 available,再用 vmstat 1 5 查看 si、so,判断系统现在是不是还在频繁换页。然后用脚本找出占用 Swap 的进程。如果业务正常、没有持续换页,就先观察;如果接口变慢、换页很频繁,就继续查具体的内存压力来源。

06 面试高频追问与应答预案​

Q1:Swap 满了,是不是物理内存也满了?​

不是。Swap 里的数据可能是以前内存紧张时换进去的。后来物理内存释放了,Swap 仍然保持占用。

Q2:free 和 available 有什么区别?​

free 是完全没有被使用的内存;available 还把一部分能回收的缓存算进去了,更适合判断现在还能给程序提供多少内存。

Q3:si 和 so 都是 0,说明什么?​

说明这段采样时间里基本没有数据在物理内存和 Swap 之间搬来搬去。如果以后程序要用到 Swap 中的数据,还是需要重新读取,会带来一定延迟。

Q4:为什么进程的 VmSwap 加起来,和 free -h 对不上?​

因为两个数据的统计范围不完全相同。free -h 看整台机器,VmSwap 看单个进程,而且部分共享内存的交换占用不会计入 VmSwap。

Q5:把 vm.swappiness 设置为 0,是不是就不用 Swap 了?​

不是。它影响 Linux 使用 Swap 的倾向,但并没有关闭 Swap。是否使用 Swap 还和内存压力、内核行为有关。

Q6:能不能用 swapoff -a 清理 Swap?​

可以停用 Swap,但要先确认有足够的物理内存。停用时可能需要把大量数据搬回内存;内存不够时可能失败或触发 OOM。生产环境先查原因,再决定是否操作。

Q7:Swap 占用高和频繁换页,有什么区别?​

Swap 占用高,是指里面放了很多数据;频繁换页,是指数据正在不断地从内存写到 Swap,或者从 Swap 读回内存。前者看占用量,后者看 si、so。

07 Swap 占用进程自动化诊断脚本(check_swap.sh)​

这个脚本会列出正在使用 Swap 的进程,显示 PID、名称和占用量,并按占用量从大到小排序。它只负责查看信息,不修改任何系统配置。

#!/usr/bin/env bash
# check_swap.sh:查看各进程的 Swap 占用排行
set -u

tmp=$(mktemp) || exit 1
trap 'rm -f "$tmp"' EXIT

for dir in /proc/[0-9]*; do
    pid=${dir##*/}
    [[ -r "$dir/status" ]] || continue
    kb=$(awk '/^VmSwap:/ {print $2; exit}' "$dir/status" 2>/dev/null) || continue
    [[ "$kb" =~ ^[0-9]+$ ]] || continue
    (( kb > 0 )) || continue
    name=$(cat "$dir/comm" 2>/dev/null) || name='unknown'
    mb=$(awk -v x="$kb" 'BEGIN {printf "%.1f", x/1024}')
    printf '%s\t%s\t%s\n' "$pid" "$name" "$mb" >> "$tmp"
done

printf '%-10s %-24s %12s\n' 'PID' 'PROCESS' 'SWAP(MB)'
sort -t $'\t' -k3,3nr "$tmp" | head -n 20 | awk -F '\t' '{printf "%-10s %-24.24s %12s\n", $1, $2, $3}'

使用方法​

保存为 check_swap.sh:

chmod +x check_swap.sh
sudo bash check_swap.sh
输出示例:
PID        PROCESS                      SWAP(MB)
2446       python3                         974.9
1392       dockerd                           9.6
969        containerd                        5.4
想查看某个进程的完整启动命令,可以执行:
ps -p 2446 -o pid,args

一次查看全部关键信息​

free -h
swapon --show
vmstat 1 5
sudo bash check_swap.sh
这些命令可以直接用于日常排查。check_swap.sh 只查看进程状态,不会清理 Swap,也不会终止进程。