Linux Swap 虚拟内存实战:禁用、扩容与 OOM 排障 📅 发布时间:2026/9/18 2:23:33 👁 浏览次数: 给一台跑了半年的服务器关掉 Swap 分区本以为能少点磁盘 IO、让服务响应更快结果第二天早上监控告警几个 Java 进程被内核直接干掉了。这件事让我把 Linux 虚拟内存这套机制从头到尾又捋了一遍。Swap 分区也就是常说的虚拟内存在 Linux 上从来不是内存不够时的备胎这么简单它牵扯到页回收策略、内存水位线、OOM Killer 的判定逻辑甚至会影响你开机能不能进系统。这篇内容我打算把禁用、添加、修改 Swap这三件事拆开讲清楚包括查看现状的正确命令、分区逻辑卷swapfile 三种方案怎么选、fstab 和 systemd 到底谁在管这件事以及我自己踩过的几个比较难受的坑。适合刚接手 Linux 运维的同学也适合已经会敲mkswap但不太清楚背后逻辑的老手对照着检查一遍。1. 别急着敲 swapoff先弄明白 swap 在这台机器上干的什么活很多人对 swap 的第一印象是内存用完了才轮到它这个理解会直接导致错误操作。真实的机制是内核有一批不常访问的内存页匿名页比如进程堆栈、堆数据与其一直占着物理内存不如把它们换出到磁盘上把物理内存腾给更热的页和页缓存。这个过程叫页回收swap 就是匿名页的回收目的地。注意关键词是不常访问不是内存不够。也就是说一台内存看起来还很宽裕的机器正常情况下也会有几十到几百 MB 的 swap 占用这是完全健康的状态。1.1 swap 是页回收的缓冲池不是 OOM 的开关内核在做内存回收时有个水位线概念free 内存低于 low 水位就开始轻量回收低于 min 水位才会触发更激进的动作。swap 存在的意义是让回收器多一个选择——没有 swap 的机器匿名页无法换出回收器只能去砍页缓存文件页砍到最后没得砍了就直接触发 OOM Killer。有 swap 的机器匿名页可以换出去页缓存得以保留文件读写性能不会因为一次内存峰值就崩掉。这也是为什么关掉 swap 提速这个说法在大多数场景下是错的。你删掉 swap省下的是那点磁盘 IO代价是页缓存更早被回收磁盘随机读变多极端情况下进程直接被 kill。真正该关 swap 的场景其实很窄后面第 5 节我会具体列出来。1.2 什么情况下该减 swap什么情况下必须加我的判断习惯是先看两个指标si/soswap in/out 速率和pgmajfault主缺页次数。如果 so 长期大于 0 且机器明显卡顿说明 swap 设备本身太慢比如机械盘、网络存储这时候要做的是换设备或者上 zram而不是无脑扩容。如果内存确实吃紧、swap 也没剩多少、dmesg 里已经开始出现 OOM 日志那就该加了。反过来以下几种情况我会主动考虑减小甚至禁用 swap数据库这类对延迟极度敏感的服务且机器内存足够宁可让它 OOM 也不愿意让它偷偷换页导致查询抖动容器宿主机上跑 Kubernetes节点层面禁用 swap 是 kubelet 的默认要求嵌入式设备用的是 eMMCSD 卡频繁写 swap 会加速闪存磨损。2. 摸清现状三条命令看清 swap 的容量、位置和真实压力动手之前必须先把现状摸清楚。我发现不少同学只会敲free -h看到 Swap 那一行是 0 就以为没开 swap其实未必。2.1 free、swapon、/proc/swaps 看到的东西并不完全一样这三者看到的数据源是一致的但呈现粒度不同命令能看到什么适用场景free -h总容量、已用、可用不带设备信息快速判断有没有 swap、压力大不大swapon --show每个 swap 设备的路径、类型、大小、已用、优先级定位 swap 挂在哪个分区或文件上cat /proc/swaps同上但格式更原始脚本里更好解析写自动化脚本时用lsblk -f块设备树的文件系统类型能看出哪个分区标了 swap确认分区是否真的被 mkswap 过free -h里的 used 计算方式容易让人误解它把 tmpfs、共享内存等也算进去了所以别拿 used 当唯一依据。更可靠的是看/proc/meminfo里的 SwapTotal 和 SwapFree再配合vmstat 1 5观察一段时间内的 si/so。注意vmstat第一行是自开机以来的平均值一定要看后面的实时行我见过有人拿第一行数据下结论结果完全判断反了。2.2 判断 swap 是假忙还是真扛不住一个实用的经验值如果vmstat里 si/so 持续在几十 KB/s 以内同时pgmajfault不高说明只是正常的页回收不用管。如果 si/so 稳定在几 MB/s 以上同时r运行队列和b阻塞队列都在涨那就是真扛不住了得从内存容量或 swap 设备性能两个方向查。还要看cat /proc/vmstat | grep -E pswpin|pswpout这两个是累计值隔几十秒采样两次做差值比看瞬时值靠谱。提示云主机上的 swap 可能是块网络盘比如某些弹性块存储IOPS 很低。这种设备上 si/so 一高整机响应就会断崖式下跌比没有 swap 还难受。2.3 分清 swap 背后挂的是分区、逻辑卷还是文件这一步决定了你后面用哪套操作流程。判断方式很简单swapon --show lsblk -f blkid | grep -i swap如果输出的是/dev/sda2这种就是分区或逻辑卷LVM 下会是/dev/mapper/vg0-swap。如果是/swapfile、/swap.img这种路径那就是文件形式。Ubuntu 从 18.04 开始默认用/swap.img早期是/swapfileDebian 和大多数 Kali 的默认安装还是给一个 swap 分区。容器里的 swap 情况更特殊通常直接继承宿主机。3. 加 swap分区、逻辑卷、swapfile 三条路怎么选三条路都能用区别在于灵活性和操作成本。3.1 swap 分区的完整创建流程这是最传统的做法性能最好没有文件系统层的开销缺点是后期调整麻烦。# 假设新加了块盘 /dev/sdb先看看现状 lsblk /dev/sdb # 用 fdisk 建分区 fdisk /dev/sdb # n - 新建分区 # p - 主分区选默认起始扇区 # 大小按需填比如 4G # t - 改类型输入 19Linux swap或 82旧编号 # w - 写入 # 让内核重新读取分区表 partprobe /dev/sdb # 或者 partx -u /dev/sdb # 格式化并启用 mkswap -L SWAP /dev/sdb1 swapon /dev/sdb1 # 验证 swapon --show这里有个细节经常被忽略fdisk写完分区表之后必须让内核重新读一次否则/dev/sdb1这个设备节点可能根本不存在。partprobe在分区正在被使用的盘上会失败这时候用partx -u更稳。另外mkswap会覆盖分区上的原有数据操作前一定要blkid确认这个设备上没有别的东西。如果要指定优先级多块 swap 设备时控制使用顺序加-p参数swapon -p 10 /dev/sdb1 swapon -p 5 /dev/sdc1数值越大优先级越高内核会优先用高优先级的设备。SSD 和机械盘混用时这个参数很有用。3.2 LVM 环境下扩 swap 的顺序不能反LVM 的好处是理论上可以动态扩但 swap 逻辑卷的扩容有个卡点mkswap会重写整个卷头所以必须先swapoff。# 1. 先关掉这个 swap 卷 swapoff /dev/vg0/swap # 2. 扩逻辑卷 lvextend -L 4G /dev/vg0/swap # 3. 重新格式化注意会清空原有 swap 数据但 swap 数据本来就是临时的不影响 mkswap /dev/vg0/swap # 4. 重新启用 swapon /dev/vg0/swap第 1 步的swapoff会把已换出的页全部读回内存如果这台机器内存已经紧张、swap 里存了好几个 G这一步有可能直接触发 OOM。所以扩 swap 的正确姿势是先确认内存余量大于 swap 已用量再操作如果不行就先临时加一个 swapfile 兜底把内存水位降下来再动原卷。3.3 swapfile 方案fallocate 和 dd 到底选哪个swapfile 的最大优势是灵活——加一个文件就行不用碰分区表。但它有个非常经典的坑# 看似没问题的写法 fallocate -l 4G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile # 报错swapon: /swapfile: swapon failed: Invalid argument原因是fallocate在 ext4 上分配的是未写入的区段unwritten extents文件在磁盘上没有真正分配连续的块swap 子系统不接受这样的文件。解决办法是用dd老老实实写dd if/dev/zero of/swapfile bs1M count4096 statusprogress chmod 600 /swapfile mkswap /swapfile swapon /swapfileXFS 上fallocate反而没问题所以这个坑只在部分文件系统上出现很容易让人以为是自己命令敲错了。我个人的做法是统一用dd慢一点但一定不会出错。另外chmod 600这步绝对不能省swap 文件权限过宽时swapon会直接拒绝加载这是出于安全考虑——swap 里可能残留进程内存数据。如果要转移虚拟内存比如把 swapfile 从系统盘挪到数据盘swapoff /swapfile # 改 fstab 里的路径或者删掉旧的换新的 rm /swapfile dd if/dev/zero of/data/swapfile bs1M count4096 statusprogress chmod 600 /data/swapfile mkswap /data/swapfile swapon /data/swapfile3.4 写进 fstab 才算真正生效前面所有操作都是临时的重启就没了。必须写进/etc/fstab# 分区或逻辑卷用 UUID 更稳 UUIDxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx none swap sw 0 0 # swapfile 直接写路径 /swapfile none swap sw 0 0 # 优先级和 swappiness 也可以写在挂载选项里 UUIDxxxx none swap sw,pri10 0 0这里我强烈建议两点一是用blkid拿到的 UUID 而不是设备路径因为设备名可能因为插拔顺序变化二是加nofail选项写成sw,pri10,nofail。注意fstab 里写错一行重启后系统可能直接进 emergency mode。swap 是可选资源加了 nofail 之后即使这块盘没挂上系统也能正常起来。4. 改大小之前先算清楚到底要多少swap 该给多大这个问题没有标准答案但有几个业界常用的参考口径。4.1 容量估算的几种口径内存大小传统经验值等于内存有休眠需求现代实践8G 以上≤ 2G2 × 内存2 × 内存2G2G ~ 8G等于内存等于内存2G ~ 4G8G ~ 64G0.5 × 内存1.5 × 内存4G ~ 8G 64G4G ~ 8G不建议休眠4G核心逻辑是如果要用休眠hibernateswap 必须能装下全部物理内存内容这个没得商量。如果不要休眠那 swap 的作用只是给页回收器留个缓冲区8G 以上内存的机器给 2~4G 基本够用给太多反而浪费磁盘。云主机的情况要单独考虑。很多云厂商的镜像默认给 2G 或者干脆不给 swap这时候你需要判断业务内存使用是否平稳。如果是突发的批处理任务加个 4G swapfile 能有效防止偶发 OOM。4.2 调整已有 swap 的三条路径扩容分区用fdisk删了重建数据无价值直接重做LV 用lvextendmkswapswapfile 直接建个更大的文件替换。缩容没有在线缩容的说法只能swapoff后重建。swapfile 直接删了重建更简单。重建先swapoff所有 swap清理 fstab然后按第 3 节的流程重来。缩容时最容易忽略的是/etc/fstab。删了 swap 分区但忘了删 fstab 那一行下次开机启动时 systemd 会尝试挂载失败如果没加 nofail 就会卡在启动流程里需要进单用户模式或者用救援盘修。4.3 swappiness 和 vfs_cache_pressure比容量更影响体感vm.swappiness控制内核在回收内存时有多倾向于换出匿名页swap而不是回收文件页page cache。默认值是 60意思是两类回收大致平衡。# 临时调整 sysctl vm.swappiness10 # 永久生效 echo vm.swappiness 10 /etc/sysctl.d/99-swap.conf sysctl -p /etc/sysctl.d/99-swap.conf经验值参考桌面机、交互式应用10~30减少无谓的换页响应更跟手数据库、缓存型服务1~10尽量保住热内存批处理、内存波动大的机器可以保留 60 或用 100让内核大胆换页用磁盘换内存空间。另一个参数vm.vfs_cache_pressure控制内核回收 dentry 和 inode 缓存的力度默认 100。如果你有大量小文件读写比如代码仓库、邮件服务器把它调到 50 左右可以让目录缓存保留更久文件遍历速度明显变快。这两个参数配合调整效果通常比单纯加 swap 容量好得多。5. 禁用与移除 swap顺序错了会很难受禁用 swap 的命令只有一句swapoff但用错顺序会导致服务雪崩。5.1 swapoff 的正确姿势与内存回填风险# 关闭单个设备 swapoff /dev/sdb1 # 关闭所有 swapoff -a执行swapoff时内核要把该设备上的所有已换出页重新读回物理内存。如果 swap 里存了 6G而当前 free 内存只有 3G那么剩余部分只能靠回收页缓存来腾地方整个过程会让机器 IO 飙升、响应变慢最坏情况直接 OOM。所以执行前一定要先看一眼grep -E SwapTotal|SwapFree|MemAvailable /proc/meminfoMemAvailable减去SwapTotal - SwapFree还有余量才安全。如果不够先关掉几个不重要的服务腾内存或者先加一块临时 swap。5.2 彻底删除三个地方都要清干净只敲swapoff是不够的重启还会回来。完整的清理链路运行时swapoff -a开机自启编辑/etc/fstab注释掉或删除对应行如果用的是独立 systemd unitsystemctl disable xxx.swap磁盘层面swapfile 直接rm分区用fdisk删除后partprobeLV 用lvremove可选如果是整块盘专门做 swapwipefs -a /dev/sdb1抹掉签名避免被其他工具误识别清理完之后验证swapon --show # 应该没有输出 systemctl --typeswap # 应该没有 active 的 swap unit cat /proc/swaps # 应该只剩表头systemd 会在/run/systemd/generator.late/下根据 fstab 自动生成.swapunit所以改完 fstab 后如果不想重启可以执行systemctl daemon-reload systemctl stop xxx.swap来同步状态。5.3 用 zram 或 zswap 替代传统 swap如果你的目的是减少磁盘 IO 又要防 OOM现代做法是上压缩内存。这跟 Windows 上的内存压缩是同一类思路只是 Linux 侧有两条路线zram在内存里划一块区域当块设备写进去的数据自动压缩压缩比通常能到 2~3 倍。相当于用 1G 内存换来 3G 的 swap 空间而且完全没有磁盘 IO。zswap一个前端压缩缓存换出的页先压缩存在内存里实在压不下才写到真正的 swap 设备。它需要你有一个真实的 swap 后端。zram 的配置大致是这样modprobe zram # 创建一个 4G 的 zram 设备 zramctl --find --size 4G --algorithm zstd # 假设输出是 /dev/zram0 mkswap /dev/zram0 swapon -p 100 /dev/zram0在 Fedora、Arch 这类发行版上直接用zram-generator或systemd-zram-setupzram0.service更省事写个配置文件就自动管理了。嵌入式设备和小内存 VPS 上zram 的收益非常明显——我有一台 1G 内存的机器上了 zram 之后编译小项目再也没被 OOM 打断过。6. 几个真实踩坑现场的排查链路前面讲的都是正常流程但真实环境里的问题基本都是流程之外的东西。6.1 fstab 写错导致开不了机现象重启后卡在A start job is running for /dev/disk/by-uuid/xxxx等 90 秒后进入 emergency mode。排查顺序在 emergency mode 里先看journalctl -xb | grep -i swap找到具体报错blkid确认 UUID 对不对很多时候是复制粘贴时少了一位检查设备是否存在lsblk如果这块盘根本没插上那就属于 nofail 该管的场景临时修复mount -o remount,rw /编辑 fstab 加nofail然后reboot。根治方案就是把所有非关键挂载都加上nofail并把 timeout 从默认的 90 秒调低到 10 秒左右在 fstab 里写x-systemd.device-timeout10。6.2 btrfs、overlay、容器里的 swapfile 为什么起不来btrfs 文件系统上直接创建 swapfile 会失败报Invalid argument因为 btrfs 的 CoW 特性和 swap 的固定块映射要求冲突。解决办法是创建时用chattr C关闭 CoWtruncate -s 0 /swapfile chattr C /swapfile fallocate -l 4G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile容器里的情况更复杂。Docker 默认不允许容器内启用 swap需要宿主机的 cgroup 配置配合而且大多数编排系统本身就不推荐容器用 swap。在容器里看到swapon: Operation not permitted基本就是权限和 cgroup 限制不是配置写错了。虚拟机里装 Linux 时如果选了 LVM 自动分区安装器一般会自动给一个 swap 逻辑卷这时候就别再手动加 swapfile 了容易变成两份 swap 抢优先级。6.3 swap 莫名满掉从 smem 到 smaps 的定位过程有一次我发现 swap 用了 90%但free -h显示物理内存还剩不少非常反直觉。定位过程是这样的# 1. 看哪些进程占用 swap 最多 smem -t -k -s swap | tail -20 # 没有 smem 的话用这个脚本遍历 for pid in /proc/[0-9]*; do s$(awk /VmSwap/{print $2} $pid/status 2/dev/null) [ -n $s ] [ $s -gt 0 ] echo $s ${pid#/proc/} $(cat $pid/comm 2/dev/null) done | sort -rn | head -20结果是几个长期运行的 Java 进程每个占了几百 MB 的 VmSwap。原因是 JVM 的堆被换出后GC 需要访问这些页时又得换回来形成反复换入换出的抖动。这种场景下的处理方式和内存不够完全不同要么调 JVM 参数-XX:AlwaysPreTouch让堆在启动时就全部落盘要么把 swappiness 降到 1让内核尽量别碰匿名页。这个案例说明一件事swap 使用率高不一定是内存不够也可能是某类进程的内存访问模式被内核误判为冷页。用/proc/pid/smaps里的 VmSwap 字段可以精确定位到具体是哪几个进程比看总量的free有用得多。7. 不同发行版与运行环境的差异处理同样一条mkswap在不同发行版和环境下表现可能不一样这也是很多教程照着做却失败的原因。7.1 Debian 系、Kali 与 Ubuntu 的默认行为Ubuntu 从 18.04 开始在安装时默认创建/swap.img文件而不是分区容量跟内存挂钩小内存机器给到内存大小大内存机器固定 2G 左右。所以在 Ubuntu 上做扩容你操作的是文件不是分区。Kali 和 Debian 的默认安装一般走分区方案用swapon --show能看到/dev/sda5这类设备。做 Kali 学习笔记类的实验时如果想让快照体积小一点把 swap 换成一块小的 swapfile 是常见做法因为虚拟化平台快照会把 swap 分区的内容也一起存下来。顺带提一句debootstrap或者自定义安装出来的最小系统往往压根没有 swap需要手动补。这类系统里 udev 规则可能不完整partprobe之后设备节点没出现的情况我都遇到过这时候partx -a /dev/sda是有效的补充手段。7.2 云主机、虚拟机与嵌入式设备的取舍环境建议方案理由云主机SSD2~4G swapfile块存储 IOPS 有限swapfile 调整灵活本地虚拟化分区或 LV快照管理和容量规划更清晰嵌入式eMMC/SDzram 优先避免闪存写入磨损容器宿主通常禁用编排系统的调度假设需要小内存 VPS1G 以下zram 小 swapfile压缩比高防 OOM 效果好云上还有个细节部分云厂商的镜像在/etc/cloud/cloud.cfg里配置了 swap 的自动管理逻辑你手动改完之后重启会被覆盖回去。遇到改了 fstab 重启又变回去的情况先翻一下 cloud-init 相关配置。7.3 面试里常被追问的几个点关于 swap 的面试题其实翻来覆去就那几个答的时候能把原理串起来就很出彩swap 满了会怎样不是立刻 OOM。swap 满之后匿名页无法换出回收压力全落到页缓存上页缓存被砍完就触发 OOM Killer它会挑oom_score最高的进程杀。可以调vm.overcommit_memory和 oom_score_adj 来影响这个行为。为什么数据库服务器建议关 swap因为换页会让查询延迟出现无法预测的毛刺而这种抖动在延迟敏感型业务里比直接报错更难排查。swappiness 设成 0 是不是就完全不换页了不是。设为 0 只是在同等条件下优先回收文件页当内存实在回收不出来时内核仍然会换出匿名页。想彻底不换只能swapoff。swap 和 tmpfs 有什么区别一个往磁盘写一个往内存写tmpfs 占用的是内存和 swap 空间如果 swap 存在tmpfs 的页也可以被换出这点很多人答不上来。我个人在实际操作中的体会是swap 相关的操作本身都不难难的是判断该不该动和动完之后怎么验证。每一次改完 fstab 或者调整 swappiness我都会重新跑一遍free -h、swapon --show、vmstat 1 10这套组合观察至少一分钟的实时数据确认 si/so 和内存水位都在预期范围内再收工。还有个小技巧是把变更前的sysctl -a | grep vm.和cat /proc/swaps输出存一份到/root/下出问题的时候对照着回滚比凭记忆猜快得多。