
事情往往是这样的Ubuntu 服务器或开发机用得好好的突然某天开始频繁卡顿网站超时数据库连接断开连终端都像冻住一样。我一开始以为是程序写得有问题折腾半天最后翻系统日志才发现是 OOM killer 在“裁员”——物理内存不够swap 又小得可怜系统只能强行杀掉进程换空间。这种被 swap 不足逼到深夜救火的经历我踩过不止一次。后来把 swap 扩容之后同样的负载跑得安安稳稳。这篇文章就按我自己的实操路径来把在 Ubuntu 上修改和扩容 swap 的完整过程写下来为什么要扩、怎么判断、选文件还是分区、每一步具体命令是什么、改完怎么验证包括踩过的坑和排查方法都会交代清楚。有同样内存焦虑问题的朋友可以直接把命令复制过去改一下参数就能用。1. 为什么要给 Ubuntu 扩容 swap1.1 swap 在系统里的真实作用先简单理解 swap。物理内存RAM是你电脑上的“工位”速度快但数量有限。当所有工位坐满还有进程想挤进来的时候Linux 会把那些暂时不怎么活跃的内存页搬到磁盘上的“仓库”里存放这个仓库就是 swap。等进程需要这些数据时再把它从磁盘搬回来。工位不够可以临时去仓库取东西但仓库本身也是有限的。如果仓库也满了新申请内存的进程就无路可走内核只能启用 OOM Killer内存溢出杀手挑一个它认为最该清理的进程直接杀掉。这也就是为什么你会看到数据库突然崩、编译进程凭空消失而dmesg日志里往往写着Out of memory: Kill process。很多人疑惑明明还有可用内存swap 怎么还是满了因为 Linux 内存管理有自己的策略。它不光把物理内存塞满之后才用 swap也会根据vm.swappiness参数来决定多积极地使用 swap。默认值通常是 60意味着系统会主动把一部分不活跃的内存页换到磁盘给 page cache文件缓存腾地方。Page cache 能提高文件读写速度所以内核愿意这么做。但如果 swap 本身只有 1G 或 2G而内存需求一涨这个缓冲池很快就见底了。1.2 怎么判断当前 swap 该不该动不是所有机器都必须扩容 swap。如果你的内存常年充裕free -h里 available 一直很高其实不用折腾。但出现下面几种信号就值得动手用free -h查看时Swap 的 used 长期超过总量 80%甚至接近 100%。运行大型编译、容器批量启动、Java 应用等内存峰值明显的任务时系统反应迟钝进程反复被杀。dmesg -T | grep -i oom有输出说明确实发生过 OOM。云服务器监控里看到 swap 读写频繁磁盘 IO 被大量换页拖慢。我个人的经验是如果机器内存只有 2G 或 4G尤其跑数据库、代码编译、浏览器多标签这类应用扩容 swap 是性价比最高的“加内存”方式。但如果内存已经有 16G 以上swap 还一直被打满那更值得关注的是应用本身的内存占用而不是一味加大 swap。2. 扩容前的准备工作与方案选型2.1 先把当前的 swap 情况摸清楚动任何系统配置之前先看清现状。我一般会在终端里连续跑几条命令free -h swapon --show lsblk -f df -h /free -h能直接看物理内存和 swap 的总量与用量。swapon --show则会列出当前所有启用的 swap 设备或文件并显示类型file表示 swap 文件partition表示真正的 swap 分区。lsblk -f可以看清磁盘分区结构df -h /用来确认根分区剩余空间够不够放大的 swap 文件。举个例子我常看到这样的输出NAME TYPE SIZE USED PRIO /swapfile file 2G 1.8G -2这说明机器当前用的是/swapfile这个文件容量 2G已经用掉 1.8G明显不够就是要处理的场景。还有一个小技巧查看/proc/meminfo可以拿到更精确的内核数据grep -E SwapTotal|SwapFree /proc/meminfo另外扩容 swap 文件需要磁盘空间所以别忘了确认df -h。有时候根分区只剩几百兆那就要考虑把 swap 文件放到别的挂载点或者先清理磁盘否则后面创建文件会直接报磁盘空间不足。2.2 选 swap 文件还是扩 swap 分区标题里写的是“修改 swap 分区”但这正是很多人容易走弯路的地方。实现 swap 有两大方式swap 分区和 swap 文件。两者的内核机制是一样的区别只在于存放形式。对比维度swap 分区swap 文件扩容灵活性需要重新分区、可能调整分区表直接换一个更大的文件即可操作风险分区操作失误可能影响整个磁盘风险相对更低出错最多影响 swap性能物理上连续通常稍好和分区差别不大现代 SSD 上几乎无感兼容性最通用休眠支持更稳文件系统要支持 swap 文件常规 ext4/xfs 没问题适用人群严格要求的服务器、需要休眠绝大多数开发机、虚拟机和云主机从实践角度看我强烈推荐用 swap 文件方式扩容。理由很直接不用碰分区表不用背数据风险以后想再调大调小只需要停掉旧文件、创建新文件、改一行 fstab 配置五分钟就能完成。如果你的系统原本用的就是 swap 分区也不一定要在原分区上继续扩。完全可以保留它不动再额外创建一个 swap 文件并启用好处是操作期间内存压力不至于瞬间爆掉。如果坚持要在原分区上扩容本文第 3.4 节会有一种相对安全的处理思路但请一定先备份重要数据。3. 实操记录我用 swap 文件完成扩容的完整流程3.1 安全关闭旧的 swap扩容前第一件事是处理当前已经挂载的旧 swap。这里有讲究如果旧 swap 几乎没被使用直接执行下面命令关闭即可sudo swapoff -a但如果旧 swap 已经用了不少空间而你的物理内存又没有很多空闲此时swapoff -a很可能失败报Cannot allocate memory。因为关闭 swap 时内核需要把 swap 里的数据搬回物理内存内存不够就关不掉。这种情况下更稳妥的顺序是先创建新的 swap 文件并启用让系统同时拥有新旧两个 swap然后把旧的关闭。我先演示最常见的安全流程假设你的根目录还有足够空间我们准备把 swap 扩到 8G。如果之前有旧 swap 文件并关闭成功可以顺手把它删掉但要先确认 fstab 里没有残留配置。建议先把 fstab 备份一下sudo cp /etc/fstab /etc/fstab.bak这里非常值得注意不做备份直接改 fstab万一配置写错重启后系统可能起不来到那时候只能进恢复模式或单用户模式修非常麻烦。备份是对自己负责。3.2 创建新的 swap 文件并启用创建 swap 文件有两种常见命令。第一种是sudo fallocate -l 8G /swapfile优点是一个命令瞬间完成。但 fallocate 创建的是“空洞文件”在 ext4 上通常没问题但某些文件系统或某些应用场景下可能产生性能隐患。所以我个人更倾向用 dd 方式虽然慢一点但更稳妥sudo dd if/dev/zero of/swapfile bs1M count8192 statusprogress这里的参数解释一下if/dev/zero表示从零设备读数据of/swapfile是输出文件bs1M表示每次写 1MiBcount8192就是写 8192 块正好 8GiB。如果想要 4G就把 count 改成 4096想要 16G就改成 16384。dd 命令会真实占用磁盘空间创建过程中能看到进度。紧接着设置权限这一步别省sudo chmod 600 /swapfile为什么要 0600 权限swap 文件里可能存放进程的敏感数据如果其他用户可读就意味着他们能尝试读取被换出内存的数据。内核在启用 swap 文件时如果发现权限过宽还会弹出insecure permissions警告。安全习惯不是小题大做。然后格式化为 swap 文件系统并启用sudo mkswap /swapfile sudo swapon /swapfile启用后立刻验证一下free -h swapon --show这次应该能看到 8G 的 swap 文件已经生效且 used 几乎为 0。3.3 配置开机自动挂载手动swapon只会对本次开机生效。要让它重启后依然存在必须写入/etc/fstab。执行echo /swapfile none swap sw 0 0 | sudo tee -a /etc/fstab这一行字段的含义是设备路径/swapfile挂载点不用写因为 swap 不挂到目录写none文件系统类型是swap挂载选项sw表示作为交换空间使用后面跟的0 0分别是 dump 备份标记和 fsck 检查顺序swap 都不需要。为了确保 fstab 配置没有写错我习惯在重启前先做一次挂载演练sudo swapoff -a sudo swapon -a这两条命令执行完再通过free -h和swapon --show检查如果一切正常说明 fstab 里的配置能被系统正确读取重启也不会出问题。记住这里swapon -a会被系统启动流程调用所以用它来模拟开机挂载是非常有效的测试手段。3.4 想扩原始 swap 分区的替代做法我在文章开头就建议优先用 swap 文件但如果你确实必须扩原始 swap 分区也不是不行。以常见的单块磁盘为例思路是这样的先用swapoff -a关闭分区上的 swap。用fdisk /dev/sda或parted查看分区表确认 swap 分区之后是否有连续未分配空间。如果后续空间可用删除原 swap 分区再新建一个包含更多空间的分区。删除和重建时起始扇区必须保持和原来一致否则其他分区的位置会错乱这是相当危险的操作。新建分区后执行sudo mkswap /dev/sdX重新格式化该分区为 swap再udo swapon /dev/sdX启用。最后更新 fstab 里面的 UUID。分区重新格式化后 UUID 会变旧配置会失效。用blkid查看新的 UUID替换到 fstab。这套操作繁琐且敏感尤其在磁盘上没有足够未分配空间或者 UEFI、LVM 等复杂布局下稍有不慎就会导致系统无法引导。所以我的真实建议是能用 swap 文件就别碰分区。4. 常见问题与排查实录4.1 swapoff 报 Cannot allocate memory现象是这样swapoff: /swapfile: swapoff failed: Cannot allocate memory原因很简单物理内存里已经没有足够空间来容纳 swap 中正在使用的数据。这个问题在内存较小的云主机上非常常见尤其是在你准备一次性关掉所有 swap 的时候。我的处理思路分三步。第一步运行free -h看看 available 有多大。如果 available 远小于 swap used就得先释放内存。第二步临时停掉大内存进程比如数据库、Java 服务、容器让available 涨上来再swapoff。第三步如果连停服务都做不到可以尝试释放一部分 page cache让“可回收”内存变多sudo sysctl vm.drop_caches1drop_caches会丢弃一部分干净的页缓存不影响程序本身的数据只影响文件缓存代价是之后磁盘读取可能短暂变慢。这是应急手段服务器业务高峰期慎用。最稳妥的绕法还是我前面提过的先创建并启用新 swap 文件再逐个关闭旧 swap。这样系统的总交换能力始终有保障不会出现必须立刻把数据塞回物理内存的情况。4.2 重启后 swap 没生效这个问题的罪魁祸首九成是 fstab 配置不对。系统启动时会按 fstab 自动挂载所有东西如果 swap 行写错启动流程会跳过它但不会直接拦着系统起不来。排查方法cat /etc/fstab blkid journalctl -b | grep -i swap先看 fstab 里有没有旧的UUIDxxx残留。如果原来用分区方式后来你把分区重新mkswap格式化过UUID 已经变了而 fstab 还指着旧 UUID就必须用blkid查出新值替换掉。如果是文件方式检查文件路径是否存在、权限是否是 600。journalctl也会留下线索比如swapon: cannot open /swapfile: No such file or directory这时候一般就是路径写错或者文件被删了。按我的习惯写完 fstab 后都会用swapoff -a swapon -a临时演练一遍避免把问题留给重启后的自己。4.3 swap 文件权限不安全的警告如果你用 mkswap 或 swapon 时看到类似insecure permissions 0644, 0600 suggested的提示无需惊慌这只是一个安全警告。原因是创建文件时保留了不少系统默认权限。处理非常直接sudo chmod 600 /swapfile sudo swapon /swapfile权限改成 600 后警告就会消失。如果反复没生效注意是不是文件路径写错了或者曾经用fallocate创建文件时因为某些原因没有成功覆盖旧文件权限。4.4 swappiness 参数到底该不该调swappiness是很多人都会忽略的一个调优点。它控制内核倾向使用 swap 的程度范围是 0 到 100 之间默认通常是 60。数值越高内核越主动把不活跃内存页往外换数值越低它越倾向于留在物理内存。如果你的机器物理内存充足但是 swap 使用率莫名其妙在涨很可能是默认值太高导致内核“主动”搬了不少无关紧要的页面。这时可以手动调低sudo sysctl vm.swappiness10这只是临时生效。想永久固化写入配置echo vm.swappiness10 | sudo tee -a /etc/sysctl.conf我通常建议普通桌面或开发机设 10服务器设 10~20内存吃紧的小机器保持默认 60 甚至调到 80 也行。不要盲目设成 0极端情况下可能导致内核在内存紧张时更倾向于触发 OOM而不是顺畅换页反而不利于稳定。曾经有段时间大家喜欢设置 0但后来很多内核问题讨论里都指出0 在极端场景下也有风险。适度的 swap 是缓冲垫不是累赘。5. 我自己的 swap 配置习惯和一些补充扫一遍运维和开发的机器我自己的默认策略是这样的内存 4G 以下的小机器swap 给到内存的 1 到 2 倍比如 8G 内存的笔记本swap 给 8G 左右内存 16G 以上的开发机swap 给 4G 到 8G 就够重点是活跃进程不会因为瞬时峰值被杀大型数据库服务器则不能只靠 swap内存规划和锁内存才是根本但保留 8G 左右的 swap 能应对突发流量。swap 文件放在 SSD 上确实会占用磁盘寿命但现代 SSD 的磨损控制已经很强作为 swap 用途的那点写入量基本可以忽略。比起寿命更需要注意的是在机械硬盘上跑高负载服务时swap 换页会造成明显 IO 延迟这种情况下尽量别让系统过度依赖 swap。我在实际使用中还有一个习惯扩容完成后用watch -n 1 free -h观察一段时间内存走势。如果发现 swap 使用量在几分钟内又开始上涨就需要进一步排查是哪个进程吃掉内存而不是无限扩大 swap。毕竟用磁盘当内存速度差距摆在那里仓库里取东西再快也不如工位上直接拿。还有一个小技巧可以分享如果你有多台相同配置的机器把整个流程写成脚本路径、大小、swappiness 都参数化以后重装系统或者批量初始化节点时一条命令就能完成 swap 命名、创建、权限、挂载、fstab 配置全部过程省下不少重复劳动。回到开头说的场景。我现在手上那台 Ubuntu 开发机16G 内存配 8G swapvm.swappiness调到 10跑容器、编译、桌面环境一起开再也没见过 OOM killer 在日志里“大开杀戒”。如果你也在被 swap 不足折腾得焦头烂额按这套流程处理完大概率能少扛几次深夜救火。