☰
Linux Swap 机制详解:从换页原理到 swappiness 调优与 OOM 排查
2026/10/29 11:44:05 网站建设 项目流程

1. 被误解的 Swap:它到底在解决什么问题

很多人第一次接触 Linux 的 Swap,是在装系统时看到分区界面那个"交换分区"选项,随手划了 2GB 或者 4GB 就过去了,之后再也没管过。等到某天机器卡顿,敲个free -h发现 Swap 用了一大半,第一反应往往是"内存不够了,得加内存"或者"Swap 拖慢了系统,赶紧关掉"。这两种反应都不算错,但都只看到了 Swap 的一个侧面。

先把定义摆清楚:Swap 是 Linux 内核在物理内存(RAM)之外开辟的一块"溢出缓冲区",它可以是独立的磁盘分区,也可以是普通文件。当物理内存吃紧时,内核会把一部分暂时不活跃的内存页(page)挪到 Swap 上,腾出 RAM 给更急需的进程用。等那些被挪走的页再次被访问时,内核再把它们换回内存。整个过程对用户态进程是透明的,进程自己并不知道自己的某段数据此刻躺在磁盘上。

这个机制解决的核心问题不是"内存不够用",而是"内存的分配在时间上不均匀"。举个生活化的例子:你书桌(RAM)就那么大,白天做项目 A 时摊满了 A 的资料,晚上切换到项目 B,A 的资料其实一时半会儿用不上,但你又不想扔掉。Swap 就是旁边那个文件柜——把 A 的资料先收进柜子,桌面腾出来给 B 用,等哪天又要做 A 了再翻出来。桌面越大越好,但柜子不是没用的东西,它让"桌面"这个稀缺资源能被更灵活地调度。

所以这篇内容适合谁看?如果你是刚接触 Linux 的开发者,想搞清楚 Swap 到底该不该开、开多大;如果你是运维,机器上 Swap 使用率异常却不知道从哪查起;或者你只是好奇为什么有些老机器内存不大却跑得挺稳——这些场景都会在下面展开。我会从内核的换页机制讲起,再落到swappiness、分区与文件的取舍、OOM 与 Swap 的关系这些实操层面,最后给一套可以直接抄的排查和调优流程。全程基于常见发行版的通用行为,不绑定某个特定版本。

需要先纠正一个流传很广的误解:Swap 不等于"内存不够时的救命稻草",它更像是一种内存管理的弹性手段。理解这一点,后面所有的配置决策才有依据。

2. 换页机制拆解:内核在什么时机、按什么规则动用 Swap

2.1 匿名页与文件页:Swap 只接管其中一类

Linux 的内存页大致分两类:文件页(file-backed page)和匿名页(anonymous page)。文件页背后有磁盘上的文件做支撑,比如你mmap了一个大文件、或者读进来的可执行代码段,这类页被回收时直接丢弃即可,下次访问再从文件读回来,不需要 Swap 参与。匿名页则没有磁盘后备,典型的就是进程通过malloc/brk/mmap(MAP_ANONYMOUS)拿到的堆和栈内存,进程的局部变量、动态分配的结构体都在这里。

匿名页一旦被回收就没有"原件"可恢复,所以内核必须给它找个落脚点——这就是 Swap 的用武之地。换句话说,Swap 主要服务的是匿名页。这也解释了一个现象:一台跑着大量读写文件的机器(比如文件服务器),即使内存紧张,Swap 使用量也可能不高,因为大部分内存是文件页,直接丢弃回收就行;而一台跑着很多计算型进程、堆内存巨大的机器,Swap 压力会明显更大。

理解这个区分很关键。当你看到 Swap 使用率高时,先别急着下"内存不足"的结论,要结合vmstat里的si/so(swap in/out 速率)一起看。如果 Swap 占用高但si/so长期接近 0,说明那些页只是被换出去了、一直没被换回来,系统其实很健康;真正要警惕的是si/so持续有值,那才是"换页抖动"(thrashing)的信号。

2.2 回收触发的三条路径

内核不是等到内存彻底耗尽才动手,它有一套分级的回收策略。常见触发路径有三条:

第一条是直接回收(direct reclaim)。当进程申请内存时发现空闲页低于水位线(watermark),内核会在分配路径上同步触发回收,把一些页换出去或丢弃。这条路径是"卡"的根源——进程被迫等待回收完成才能拿到内存,表现为延迟飙升。

第二条是后台回收(kswapd)。内核有个专门的内核线程kswapd,在空闲内存降到低水位时被唤醒,异步地做回收,尽量在内存真正告急前把水位拉回来。健康的系统里,大部分回收工作应该由kswapd完成,而不是让进程自己去 direct reclaim。

第三条是内存压缩(compaction)与 OOM。当回收也救不回来时,内核会尝试内存压缩整理碎片,实在不行就触发 OOM killer 杀掉占用最大的进程。Swap 在这里的作用是"延缓 OOM 的到来"——多一个换出通道,就多一分在内存压力下继续运行的可能。

这三条路径对应到监控上:/proc/vmstat里的pgscan_direct、pgscan_kswapd能看出回收主要走的是哪条路;pgmajfault反映的是需要从磁盘换回页的缺页次数。如果pgscan_direct持续增长,说明系统已经在"硬扛"内存压力了,这时候加 Swap 或者加内存都是合理方向。

2.3 水位线与 swappiness:两个最容易被误读的参数

/proc/sys/vm/swappiness是讨论最多的参数,默认值通常是 60。它的含义经常被简化成"多愿意用 Swap",更准确的说法是:在回收匿名页和文件页之间,内核偏向哪一边的权重。值越高,越倾向于把匿名页换出去、保留文件页做缓存;值越低,越倾向于丢弃文件页缓存、尽量不动匿名页。

这里有个反直觉的点:swappiness=0并不等于"完全禁用 Swap"。在较新的内核里,它只是把倾向性压到最低,当内存压力大到一定程度、文件页已经没得回收时,匿名页照样会被换出。真正想禁用 Swap 得用swapoff。很多"调优教程"让你把 swappiness 设成 0 来"提升性能",这在数据库服务器上可能合理(希望缓存尽量留在内存里),但在普通开发机上反而可能让内存压力更早触发 direct reclaim。

水位线(/proc/sys/vm/min_free_kbytes、watermark_scale_factor等)则是另一层。min_free_kbytes决定了内核保留多少"应急内存",它间接影响kswapd何时被唤醒。这个值调太小,回收启动太晚,容易直接进入 direct reclaim;调太大,又浪费可用内存。一般按物理内存的一个小比例来设,具体数值下面实操部分会给参考。

提示:改swappiness之前先用vmstat 1观察一段时间的si/so,确认问题真的出在换页策略上,而不是盲目跟风调参。很多"卡顿"其实是 IO 或 CPU 的问题,跟 Swap 没关系。

3. Swap 分区还是 Swap 文件:选型背后的取舍逻辑

3.1 两种形态的本质差异

Swap 分区是一块独立的磁盘分区,类型标记为82(MBR)或对应的 GPT 类型码,用mkswap格式化后由内核直接管理。Swap 文件则是文件系统里的一个普通文件,同样用mkswap处理,再swapon挂上。两者在内核看来都是"一块可以按页读写的块设备",功能上等价,差异主要在管理和性能细节上。

分区的好处是没有文件系统层的开销,IO 路径更短,理论上换页延迟略低;坏处是大小固定、调整麻烦,扩容要动分区表,缩容更是要重建。文件的好处是灵活——想加就fallocate一个文件、mkswap、swapon,想删就swapoff后rm,云主机上尤其方便;坏处是文件本身要经过文件系统,碎片化严重时性能会打折,而且某些文件系统(如早期的 btrfs)对 Swap 文件支持有额外限制。

我的经验是:物理机、对换页延迟敏感的场景优先用分区;云主机、容器环境、需要动态调整的场景用文件。现在大部分云厂商的镜像默认就是 Swap 文件,因为镜像制作和扩容都简单。

3.2 大小怎么定:别迷信"内存的两倍"

老资料里"Swap 大小 = 2 倍物理内存"的说法来自内存普遍只有几百 MB 的年代,那时候进程的匿名页占比高、内存又小,确实需要大 Swap 兜底。放到今天,这个规则基本失效了。更实用的判断维度是:

场景物理内存建议 Swap 大小理由
开发机/桌面16GB 以上2~4GB 或不开内存充足,Swap 主要防极端情况
通用服务器8~32GB等于或略小于内存平衡弹性与磁盘占用
内存密集型服务64GB 以上4~8GB大内存机器换页代价高,不宜大 Swap
休眠(hibernation)需求任意不小于内存休眠要把内存内容整体写入 Swap
容器/嵌入式小内存谨慎,可能不开依赖 cgroup 内存限制而非 Swap

最后一行值得展开:容器环境里,Swap 的使用受 cgroup 的memory.swap.max控制,宿主机的大 Swap 不一定对容器可见。盲目给宿主机配大 Swap,可能让容器在内存超限时表现得更"迟钝"而不是更快被限制。这块要结合编排平台的资源限制一起看。

还有一个常被忽略的点:如果机器需要休眠(suspend to disk),Swap 必须不小于物理内存,因为休眠时整个内存镜像要写进 Swap。笔记本用户如果发现休眠失败,先查 Swap 大小够不够。

3.3 创建与启用的完整操作

以 Swap 文件为例,给一套可以直接复现的步骤。假设要加一个 4GB 的 Swap 文件:

# 1. 创建文件,fallocate 比 dd 快很多 sudo fallocate -l 4G /swapfile # 2. 权限收紧,只有 root 可读写 sudo chmod 600 /swapfile # 3. 格式化为 swap sudo mkswap /swapfile # 4. 启用 sudo swapon /swapfile # 5. 验证 free -h swapon --show

要让它在重启后自动生效,得写进/etc/fstab:

/swapfile none swap sw 0 0

这里有个坑:fallocate创建的文件在某些文件系统(如老版本 ext4 的某些配置、或启用了 copy-on-write 的 btrfs)上可能不被swapon接受,报错类似 "swapon failed: Invalid argument"。遇到这种情况改用dd写零:

sudo dd if=/dev/zero of=/swapfile bs=1M count=4096 status=progress

dd慢但兼容性好,因为它真正把数据块写下去了,文件不会带"空洞"(sparse)。fallocate只是预分配元数据,速度快但可能留下空洞,而 Swap 文件不允许有空洞。

分区方式则是先用fdisk/parted划一块分区,mkswap /dev/sdXN,再swapon,fstab 里写UUID=xxx none swap sw 0 0。用 UUID 而不是设备名,避免设备顺序变化导致挂载错乱。

注意:swapoff一个正在被大量使用的 Swap 会把里面的页全部换回内存,如果内存不够,这个过程可能触发 OOM。生产环境操作前先确认空闲内存足够,或者挑低峰期做。

4. 从 free 到 vmstat:Swap 异常的完整排查链路

4.1 先分清"占用高"和"在抖动"

排查 Swap 问题的第一步不是急着调参,而是判断当前状态属于哪一类。我习惯按这个顺序看:

free -h vmstat 1 5 cat /proc/vmstat | grep -E 'pgscan|pgmajfault|pswpin|pswpout'

free -h给出的是快照:Swap行的used是当前被换出的量。但单看这个数字意义有限,因为换出去的页可能一直没被访问,属于"冷数据",占着 Swap 反而说明内存调度得当。

vmstat 1 5每秒采样一次,重点看si(swap in,从 Swap 读回内存)和so(swap out,写入 Swap)。如果si/so长期为 0 或极小,Swap 占用再高也不用慌;如果两者持续有可观数值(比如几十上百 KB/s 甚至 MB/s),说明系统在频繁换页,这才是性能问题的信号。

/proc/vmstat里的pgmajfault是"主缺页"次数——需要从磁盘(包括 Swap)读回页才能满足的缺页。这个值持续增长,配合si高,基本可以确认换页抖动。pgscan_direct和pgscan_kswapd的对比则告诉你回收是同步硬扛还是后台异步做的。

4.2 定位是哪个进程在制造压力

确认存在换页压力后,下一步是找出"元凶"。/proc/<pid>/status里的VmSwap字段直接告诉你某个进程有多少内存被换到了 Swap:

# 按 VmSwap 排序,找出 Swap 占用最大的进程 for pid in /proc/[0-9]*; do swap=$(awk '/VmSwap/{print $2}' "$pid/status" 2>/dev/null) [ -n "$swap" ] && [ "$swap" -gt 0 ] && echo "$swap ${pid#/proc/}" done | sort -rn | head -10

这段脚本遍历所有进程,把VmSwap(单位 kB)和 PID 打出来排序。跑完你大概率会发现某个进程的 Swap 占用远超其他——它要么是内存泄漏,要么是配置的堆上限太大,要么就是长期不活跃但没被回收的"僵尸"服务。

找到进程后,用smem或者ps_mem这类工具看它的实际内存构成(RSS、PSS、USS、Swap),比ps aux的 RSS 更准。ps aux的 RSS 会把共享库重复计算,容易误判。

4.3 一个真实的排查案例复盘

说个我遇到过的场景(细节做了脱敏)。某台跑批处理任务的机器,白天负载低时一切正常,到了夜间批处理启动后开始卡,free -h显示 Swap 用了 70% 以上。第一反应是内存不够,但机器物理内存 32GB,批处理进程的 RSS 加起来才 10GB 出头,明显对不上。

按上面的链路查:vmstat 1显示si/so在批处理启动后持续有值,pgmajfault快速增长,确认是换页抖动。再用脚本按VmSwap排序,发现一个"常驻"的日志采集进程占了近 8GB 的 Swap——它平时几乎不活动,内存被换出去很正常,但它同时配置了很大的堆缓存,且有个定时任务会周期性遍历全部缓存,一遍历就把 8GB 从 Swap 全读回内存,瞬间把内存打满,触发新一轮换出,形成恶性循环。

根因不是"内存不够",而是一个不活跃进程的周期性全量访问模式与 Swap 机制冲突。解决方案是给那个采集进程的缓存设上限、改增量遍历,而不是简单加内存或关 Swap。这个案例说明:Swap 使用率高本身不是病,"高 Swap 占用 + 周期性全量换回"的组合才是。排查时一定要把访问模式和换页行为结合起来看。

4.4 常见误判清单

排查中容易踩的坑,列个表对照:

现象常见误判实际可能原因
Swap used 高内存不足冷数据被换出,属正常调度
系统卡顿Swap 拖慢IO 瓶颈、CPU 争抢、direct reclaim
swappiness=0 后仍用 Swap参数没生效内核在极端压力下仍会换出匿名页
加了 Swap 还是 OOMSwap 没用cgroup 限制、OOM 评分、overcommit 设置
Swap 文件创建失败磁盘满文件系统不支持、文件有空洞、权限问题

最后一行再强调一次:fallocate失败换dd,dd还失败就查文件系统类型和挂载选项。这些细节在报错信息里往往不直观,得靠经验补。

5. swappiness 与内存水位:调参的边界在哪里

5.1 swappiness 的合理取值区间

前面说了 swappiness 的本质是"匿名页 vs 文件页"的回收倾向权重。实际调参时,我一般按工作负载类型分三档:

  • 数据库、缓存服务(Redis、MySQL 等):倾向设低,10 左右甚至 1。这类服务的性能高度依赖内存中的热数据,希望内核尽量保留文件页和匿名页,宁可早点触发 OOM 也不要频繁换页。但注意别设 0,留一点余地应对突发。
  • 通用 Web 服务、应用服务器:保持默认 60 即可。这类负载内存构成比较均衡,默认值经过大量场景验证,没必要折腾。
  • 桌面/开发机:可以适当调高到 80~100。桌面场景下,后台挂着的编辑器、浏览器标签页很多是"冷"的,换出去能明显缓解内存压力,用户感知到的卡顿反而更少。

改法:

# 临时生效 sudo sysctl vm.swappiness=10 # 永久生效,写入 /etc/sysctl.d/99-swap.conf echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swap.conf sudo sysctl --system

用/etc/sysctl.d/下的独立文件而不是直接改/etc/sysctl.conf,好处是配置来源清晰、便于版本管理和回滚。

5.2 min_free_kbytes 与水位线的联动

vm.min_free_kbytes决定内核保留的应急内存下限,它间接决定了kswapd的唤醒时机。默认值由内核根据内存大小自动计算,通常在几十 MB 量级。调大它能更早启动后台回收、减少 direct reclaim,但会减少可用内存。

一个经验公式是取物理内存的 1%~3%,但不超过某个上限(比如 1GB)。比如 16GB 内存的机器,设 256MB 左右比较稳妥:

# 查看当前值 cat /proc/sys/vm/min_free_kbytes # 设为 256MB sudo sysctl vm.min_free_kbytes=262144

调这个参数要配合观察pgscan_direct是否下降。如果调大后 direct reclaim 明显减少、si/so更平稳,说明方向对了;如果没变化甚至更差,说明瓶颈不在回收时机上,得回头查具体进程。

5.3 什么时候该考虑关掉 Swap

关 Swap(swapoff -a)不是常规操作,只在几种特定情况下考虑:一是内存绝对充足且负载可预测的专用服务器,比如一台只跑单一内存数据库、内存预留充足的机器,关掉 Swap 能让 OOM 行为更"干脆"(直接杀进程而不是先挣扎着换页);二是排查问题时临时关闭,用来确认卡顿是否真的与换页有关;三是容器环境,某些编排配置下不希望容器使用 Swap。

关之前务必确认空闲内存大于当前 Swap 使用量,否则swapoff过程本身就会 OOM。关掉后记得从 fstab 里注释掉对应行,否则重启又回来了。

提示:swapoff是"全有或全无"的操作,没法只关某个进程的 Swap。想精细控制,得靠 cgroup 的memory.swap.max(cgroup v2)来限制特定进程组的 Swap 用量。

6. Swap 与 OOM 的边界:它救不了什么

6.1 Swap 延缓 OOM,但不阻止 OOM

很多人以为"有 Swap 就不会 OOM",这是错的。Swap 只是给匿名页多一个去处,当所有可换出的页都换完了、内存仍然不够时,OOM killer 照样会出手。而且换页本身有代价:磁盘 IO 速度比内存慢几个数量级,大量换页会让系统吞吐骤降,有时候"被 OOM 杀掉"反而比"在换页抖动中苟延残喘"体验更好——这也是为什么有些生产环境故意把 swappiness 调低、甚至关 Swap,让问题暴露得更快。

OOM killer 选择杀谁,看的是每个进程的oom_score,这个分数综合了进程的内存占用、运行时长、是否特权进程等因素。/proc/<pid>/oom_score可以查看,oom_score_adj可以手动调整(范围 -1000 到 1000,越低越不容易被杀)。给关键进程设一个负的oom_score_adj,能在内存告急时优先保住它。

6.2 overcommit 设置对 Swap 行为的影响

vm.overcommit_memory控制内核如何处理内存分配请求。默认值 0 是"启发式 overcommit",内核凭经验判断是否批准分配;1 是"总是批准",分配时不管实际有没有内存,等真正写入时才触发回收或 OOM;2 是"严格模式",按overcommit_ratio计算可分配上限,超出直接拒绝。

这个参数和 Swap 的关系在于:overcommit 越宽松,进程越容易"申请到"超过物理内存 + Swap 的虚拟内存,真正用到时才发现不够。很多"莫名其妙 OOM"的案例,根因是 overcommit 设置让进程过度乐观地申请了内存。排查时把cat /proc/sys/vm/overcommit_memory和overcommit_ratio一起看,能解释不少"内存明明够却 OOM"的困惑。

6.3 一个容易忽略的细节:Swap 的 IO 优先级

Swap 所在的磁盘如果和业务数据共用,换页 IO 会和业务 IO 抢带宽。把 Swap 放在独立磁盘(或至少独立的高性能盘)上,能减少这种干扰。如果只能用同一块盘,可以考虑用 cgroup 的 IO 控制给换页 IO 限速,避免它把业务 IO 饿死。这个细节在 SSD 普及后影响小了些,但在机械盘或共享存储上依然值得注意。

另外,Swap 文件在 SSD 上会带来额外的写入放大,长期高频换页会加速 SSD 磨损。如果机器 Swap 活动频繁,要么加内存从根上解决,要么接受这个磨损成本——别指望靠调参消除它。

7. 我在实际运维中沉淀的几条经验

聊了这么多机制和参数,最后分享几条踩过坑之后才明白的事,都是文档里不太会写的。

第一条:先看si/so,再看 Swap 占用。这个顺序能帮你避开八成误判。Swap 占用高但si/so为 0,是健康的冷数据换出;占用不高但si/so持续有值,才是真问题。养成这个习惯,能省下很多无谓的加内存和调参。

第二条:给 Swap 文件用dd而不是fallocate,除非你确认文件系统支持。我见过不止一次fallocate创建的文件swapon报 "Invalid argument",排查半天才发现是文件带空洞。dd慢那几秒,换来的是确定性。

第三条:调 swappiness 之前先想清楚工作负载的内存构成。数据库和桌面机的合理取值能差一个数量级,照搬别人的配置大概率不合适。不确定就保持默认 60,默认值是大量场景折中的结果,不会太离谱。

第四条:周期性全量访问的进程是 Swap 的"隐形杀手"。前面案例里那个日志采集进程就是典型——平时不活动被换出,一活动就全量读回,把内存打满。这类进程要么限制缓存大小,要么改成增量访问,光加内存治标不治本。

第五条:关 Swap 前先算账。swapoff需要把 Swap 里的页全部换回内存,空闲内存不够就会 OOM。操作前free -h看一眼,空闲内存大于 Swap used 再动手,或者挑业务低峰期做。

这些经验没法替代对机制的理解,但能让你在遇到具体问题时少走弯路。Swap 这个机制本身不复杂,复杂的是它和具体工作负载、IO 特性、cgroup 限制交织在一起时的表现。把free、vmstat、/proc/vmstat、VmSwap这几个观测点用熟,大部分 Swap 相关的疑惑都能自己查清楚。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询