news 2026/9/30 1:31:03

Linux swap详解:内核机制、规划策略与调优实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux swap详解:内核机制、规划策略与调优实践

Linux系统跑了几年,一说性能问题,很多人第一反应就是“swap用了多少”。但真要问swap到底怎么工作、怎么规划、怎么调优,能一口气讲清楚的人其实不多。这篇文章就是一次完整的梳理,从内核换页机制讲到分区和文件两种创建方式,再到swappiness参数、常见故障排查,把我实际踩过的坑一并交代清楚。

1. swap存在的底层逻辑:内存不够时的缓冲带

很多人对swap的理解停留在“内存不够用了,拿磁盘顶一下”,这个说法没错,但它毕竟太粗糙。真正要理解swap,得从操作系统的内存管理说起。

1.1 swap到底在解决什么问题

计算机的内存是有限的,程序运行需要进程地址空间,内核要给每个进程分配物理页帧。当物理内存被分配完,新的请求还在不断进来,系统就必须想办法腾出空间。内核的做法是把一部分暂时不用的匿名页(Anonymous Page)写入磁盘,腾出物理页帧给更活跃的进程使用。这块磁盘空间就是swap。

这里有一个关键点要区分清楚:不是所有内存页都能swap出去。文件页(File Page,比如mmap映射的文件内容、代码段)本来就有磁盘上的后备存储,回收它们是写回文件或者直接丢弃。而匿名页没有文件支持,比如进程栈、堆、写时复制产生的私有页,它们的内容只存在于内存里,如果要把物理页帧回收,就必须先把数据写到某个地方,这个“某个地方”就是swap空间。

举个生活化的例子。你住酒店房间,房间只有一张床(物理内存)。来了第二个客人(新进程),你得让一位客人去大厅沙发等着(swap),把床让给当前要用的客人。等第一位客人需要休息了(进程访问被换出的页),再让他回床上,把沙发上的另一位客人叫走(换页)。这个“酒店大厅的等待区”就是swap。

所以swap解决的核心问题不是“增加内存”,而是让系统在物理内存紧张时仍然能撑住,把分配请求延后处理,同时保证正在运行的进程不因为瞬时内存压力直接触发OOM被内核杀掉。

1.2 内核是怎么决定谁被换出去的

这部分是理解swap调优的前提。内核维护了LRU链表,把物理页分成活跃页和不活跃页两类。内存紧张时,kswapd内核线程被唤醒,它会从不活跃列表里找候选页回收。匿名页走swapout路径,文件页走回收路径。

关键机制在于水位线(Watermark)。内核为每个内存管理区设置了high、low、min三档水位。free页面数量低于low水位时,kswapd开始异步回收;低于min水位时,会同步回收,这时候进程分配内存就会被阻塞,表现为系统卡顿。回收的优先级从进程最容易重新获取的页开始,比如未修改的文件页直接丢弃,需要读回磁盘;再往后才会动到匿名页,也就是写swap。

这解释了为什么有时候内存明明还有很多,swap却已经用起来了——因为内核回收页的顺序和普通用户直觉不一样。文件页被优先回收,而一些长时间不访问的匿名页会被换到swap。这不一定就是坏事,但要结合具体的业务场景看。

另一个常见概念是记忆回收(cgroup内存回收)。在容器化环境里,cgroup有独立的内存限制,达到限制时就会触发该cgroup的回收,包括换出其中的匿名页。很多容器平台默认禁用group级别的swap,因为swap在容器里会导致延迟不可控,这也侧面说明swap的使用需要分场景看待。

2. swap的规划策略:大小、介质、形态全都要重新想

2.1 传统“两倍内存”规则还能用吗

早期文档里经常写swap大小是物理内存的1~2倍,这个规则在物理内存普遍只有几十MB到1GB的年代是有道理的。但放到今天,一台机器动辄64GB、128GB内存,如果按两倍去规划,会创建128GB甚至256GB的swap,不仅浪费磁盘空间,还会因为swap过大掩盖内存泄漏的问题——系统一直“撑着”,却没有真正暴露资源不足的真相。

我现在的经验是,不同场景要分开看。

如果机器内存小于8GB,比如老旧的笔记本、低配云主机,swap保持和内存相当或者1.5倍是合理的,因为这种机器的物理内存确实不够用,swap能兜底。

如果是常见服务器,内存32GB到64GB,swap给8GB到16GB就够了。目的是应付突发的内存峰值,而不是长期依赖swap。

如果是大内存的数据库主机或者Redis这类内存型服务,很多人会完全关闭swap。理由一是不想有任何磁盘IO拖慢访问延迟,二是有足够内存兜底。但这里我建议保留一个很小的swap,比如2GB到4GB,它的作用不是承载业务,而是给系统一个缓冲带。万一出现内存泄漏或者某段代码疯狂申请内存,小swap能争取到登录排查的时间,而不是直接触发OOM把主进程杀掉。这个经验在线上救过我几次命。

这里要补充一个休眠(Suspend-to-Disk)的场景。笔记本如果要用休眠功能,swap大小至少要等于物理内存大小,因为系统挂起前要把整个内存镜像写到swap分区。如果你的笔记本从来不休眠只睡眠,那就不受这个限制。

2.2 不同负载下的swap推荐值

我根据自己的实践经验整理了一张表,注意这只是参考基准,不是绝对标准。云厂商的虚机如果本身可以热扩容内存,swap可以跟着物理内存调整;裸金属服务器一次规划就要留足余量。

应用场景物理内存推荐swap大小说明
桌面/办公机8GB以下内存的1~2倍物理内存不足,swap兜底
桌面/开发机16GB2GB~4GB防止build等瞬时峰值
小型服务器32GB4GB~8GB止损为主,不做长期依赖
数据库主机64GB以上0~2GB尽量不swap,极少量兜底
大数据/Hadoop节点64GB以上16GB~32GBYARN任务内存波动大
笔记本(需休眠)任意>= 物理内存休眠镜像需求

2.3 swap分区还是swap文件

传统做法是独立建一个swap分区,大小在装系统时定死。这种做法优点是从内核角度看更直接,性能也更稳定,缺点是后期调整很麻烦。要么用LVM,要么得动分区表,生产环境根本没机会随便改。

swap文件则是现代Linux发行版常见的做法。在根目录创建一个固定大小的文件(mkswap格式化),然后挂载为swap。好处是大小随时可调、不依赖分区表、创建销毁简单。阿里云、腾讯云的大量云主机默认就是swap文件的方式。早期的swap文件有个性能顾虑——文件系统本身有额外开销,但现代文件系统(ext4、xfs)对swap文件的性能协商已经做得足够好,除非你在极端IO场景,否则差别感知不明显。

有一个注意点:swap文件不能放在支持稀疏文件(所谓sparse file)格式不对位置的场景,而且有些文件系统(比如某些网络文件系统)不支持swap文件。放swap文件的位置最好是本地磁盘,千万别放NFS、CIFS这类网络存储上。网络一来回本来就慢,再叠加swap的频繁IO,系统卡到没法用。

3. 实操指南:从零创建一个可用的swap空间

3.1 创建swap分区的完整流程

先看当前系统的swap情况,用free -h或者swapon --show确认现状。

free -h total used free shared buff/cache available Mem: 15Gi 2.1Gi 11Gi 123Mi 2.0Gi 12Gi Swap: 0B 0B 0B

这台机器没有任何swap空间。现在假设我要从空闲磁盘划出8GB的swap分区。

如果用fdisk做分区规划,大致流程是:fdisk /dev/sdb,n新建分区,p选主分区,大小填+8G,t改分区类型,类型ID选82(Linux swap),w保存退出。注意修改分区表之前,先用lsblk确认没有分区被挂在用,不然写入分区表会失败。

分区建好后,用mkswap格式化。

mkswap /dev/sdb1

这样就在/dev/sdb1上建立了一个swap文件系统。然后用swapon启用。

swapon /dev/sdb1

如果需要开机自动挂载,编辑/etc/fstab加入一行。

/dev/sdb1 none swap sw 0 0

这里字段的含义是:设备、挂载点(swap固定为none)、文件系统类型(swap)、挂载参数(sw)、dump标记、fsck顺序。很多人漏掉了最后两个0,会导致开机执行fsck时扫描swap设备,轻则警告重则异常。

3.2 使用swap文件的方式

swap文件创建起来更灵活,适合云主机和桌面系统。先创建一个指定大小的文件。

dd if=/dev/zero of=/swapfile bs=1M count=8192

这里的count=8192表示8GB。也可以直接用fallocate。

fallocate -l 8G /swapfile

fallocate是分配磁盘块,速度比dd快得多。但有一个坑需要知道:对某些文件系统(比如早期版本的XFS)或者叠加了压缩、去重的文件系统,fallocate分配出来的块可能不是连续的,mkswap时会失败报错。这时候退回用dd方式。

接下来设置权限,这是很多人容易忽略的安全细节。swap文件里可能残留敏感进程的数据,权限必须收紧。

chmod 600 /swapfile

然后用mkswap格式化并启用。

mkswap /swapfile swapon /swapfile

如果要用开机自启,同样在/etc/fstab添加一行。

/swapfile none swap sw 0 0

注意swap文件的路径在根目录时,根目录如果是LVM或者加密分区,开机时需要先等文件系统就绪才能挂载swap。systemd环境下这一般会自动处理,但如果是传统SysV init的老系统,可能需要加一条local-fs.target依赖。

3.3 启停、优先级与边界约束

临时关闭swap用swapoff,可以指定设备名。

swapoff /swapfile

swapoff操作是把swap里的数据页全部换回内存,所以如果此时内存已经非常紧张,swapoff会非常慢,甚至直接因为无法分配内存而失败。这个细节后面在故障排查部分会展开讲。

多个swap设备时可以设置优先级。内核会优先使用优先级高的swap,只有高优先级满了才往低优先级写。

swapon -p 100 /dev/sdb1 swapon -p 10 /swapfile

优先级数值越大越优先。可以用这个特性做分层。比如把SSD上的swap设成高优先级,把机械硬盘上的swap设成低优先级,让快的介质先接住换页压力。

4. 性能调优与监控:从看懂数据到改对参数

4.1 swappiness参数:不是越低越好

vm.swappiness是Linux内核用来控制回收匿名页和文件页时偏向程度的参数,取值范围0到100,默认通常是60。数值越大,越倾向于把匿名页换到swap腾出物理页帧;数值越小,越倾向于回收文件页缓存。很多人听到这个就立刻把swappiness改成0或1,理由是“不想用swap”,但这个操作其实值得好好想想。

我把swappiness的语义用更直白的方式解释一下。它不是一个绝对的“要不要swap”开关,它是一个比例调节阀。数值高代表内核回收内存时更积极去换出匿名页;数值低代表内核更愿意先处理文件页缓存,尽量不动匿名页。

实际场景里,swappiness=10是很多数据库和缓存服务推荐的值,因为这类应用对延迟敏感,不希望个人页被换到磁盘。桌面系统尤其是内存不太大的笔记本,我通常建议保持默认值或者稍微调低到10~20,而不是直接改成0。原因在于桌面场景会有很多后台进程长期不活跃,完全没有swap开关意味着这些进程一直占着物理内存,导致活跃程序的内存空间被挤压,实际体验反而更卡。

如果你真的想尽量少用swap,可以设置swappiness=1,这是一个比0安全的选择。因为0的含义在不同内核版本上发生过变化,早期内核0代表“除非内存完全耗尽否则不swap”,但后来内核语义有调整,0并不完全禁用匿名页回收。1则能传递一个明确意图:尽量避免,但不绝对禁止。这是我跟很多同行交流后的一致做法。

4.2 监控swap使用状态的方法

日常看swap我主要用几个命令,各有侧重。

free -h给出的是整体概览,看当前swap总量、已用量、可用量。它能快速判断是否有异常的swap占用。

$ free -h total used free shared buff/cache available Mem: 7.6Gi 3.1Gi 4.0Gi 128Mi 462Mi 1.5Gi Swap: 2.0Gi 1.8Gi 204Mi

vmstat 1是一个更细腻的观察窗口。重点看si和so两列,代表每秒从磁盘读入swap的数据量和每秒写入swap的数据量。

$ vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 1 0 1886204 4024048 512 469712 23 35 123 210 1032 2100 8 14 77 1 0

只要si和so长期不为0,说明系统在持续进行换页。如果数值很大,比如每秒几千KB甚至更高,基本可以断定交换压力已经变成主要瓶颈了。

还有/proc/meminfo里的SwapTotal、SwapFree、Dirty等字段,可以在脚本监控系统里直接读取。这里我建议重点记录si和so,而不是只看SwapUsed,因为SwapUsed高但是si/so接近0,说明只是历史进入swap的数据没被读回,系统并不在持续做换页,性能不一定会受大影响。反过来SwapUsed不高但si/so很高,说明系统正在持续发生大量换页,这个才是真正要警惕的性能信号。

4.3 针对IO和交换的优化建议

swap问题和磁盘性能强相关。我的经验是,如果业务真的依赖了swap,那么磁盘性能决定了下限。SSD上的swap和机械硬盘上的swap,体验差距是数量级的。这也是为什么很多新装机方案里,专门在SSD上划一块小分区作为swap。NVMe SSD的延迟和随机IO性能足够好,能在内存不足时续命而不至于让系统坏死。

文件系统挂载参数也能间接影响内存压力。比如给数据盘挂载时加noatime,可以减少atime更新带来的大量写IO,从而降低文件页的dirty压力,间接减少回收频率。这个参数本身和swap没有直接关系,但能降低整个系统的IO负担。

还有一个现代优化方向是zswap。它本质是一个压缩内存缓存层,放在swap的前面。当内核决定把匿名页换出时,zswap把这些页压缩后暂存在RAM里,而不是直接写磁盘。它把“写磁盘再读回”变成了“压缩存内存里再解压读回”,对高IO压力的场景效果非常显著。启用zswap的方式是内核引导参数加zswap.enabled=1,同时可以指定zpool类型比如z3fold。

使用zswap时有一个重要前提:系统中必须有swap设备存在。因为zswap只是一个前端缓存,当压缩后的数据量超过预设阈值时,内核还是会把它写到真正的swap空间。所以不能因为配了zswap就把swap去掉。

5. 常见问题与排查实录

5.1 swap用满但内存显示还有很多空闲

这个现象很常见。有人看到free输出里Swap已经用了90%,但内存的free还有好几个G,不理解为什么系统不先把空闲内存用起来。

原因在于free命令的free字段并不等于“可用于分配的物理内存”。Linux内存管理的一个特点是“内存闲着不如给文件做缓存”。操作系统会把大部分空闲内存拿来作为页缓存(page cache),加速文件读写。当程序需要内存时,这些缓存页是很容易被回收的。所以真正衡量可用内存的字段是available,而不是free。

swap被使用说明当时内核的回收逻辑认为有些匿名页优先级低于某些缓存页,把匿名页换出去腾给文件缓存并不罕见,尤其当文件缓存是热数据时。这种情况下如果你用top看了某个进程的RES并不高,那么swap里躺着的大概率是历史遗留的不活跃进程页,系统并没有卡。

但要注意另一种情况:top里可以观察到进程的RSS不变但物理内存整体紧张,而且si/so持续很高,那说明系统在做真实的交换。判断重点仍然是vmstat里的si、so值。

5.2 swapoff失败:Cannot allocate memory

运行swapoff时经常遇到这个报错。原因是内核要把swap里尚未使用的匿名页全部一次性读回物理内存。如果物理内存可用空间不够,swapoff就会因为分配页面失败而报错退出。

我第一次遇到时直接傻眼,明明free显示的可用内存不少,但swapoff还是报错。后来发现是因为可回收的文件页缓存很多,但系统的内存回收水位不允许一次性把所有缓存页都回收掉,保证最低限度的空闲页数量。所以就卡住了。

处理方法一般是这样:先把内存里不重要的进程杀掉,比如缓存类的Java后台、没有在用的开发环境。或者用sysctl vm.drop_caches=1、2、3逐级释放页缓存,这一步能腾出大量物理内存。之后再执行swapoff就会顺利很多。

如果实在腾不出来,还有一个偏门但有效的方法:先添加一个更大的临时swap文件,把原swap的数据“搬”过去,然后把旧swap关掉。

fallocate -l 16G /tempswap chmod 600 /tempswap mkswap /tempswap swapon /tempswap swapoff /swapfile rm /swapfile

这套操作本质是给数据一个中转站,让swapoff过程中不需要一次性把数据全部读回内存,而是先进了新的swap文件。在运维层面这是比较稳妥的swap缩容方式。

5.3 系统卡顿但swap占用不高,怎么定位

这种情况我遇到过很多次,尤其在一些内存看起来很大的机器上。free显示swap用了不到1GB,但整机交互就是卡。一开始我会觉得swap没背锅,但排查到最后,问题往往出在IO竞争。

工作机制上,内存回收和换页过程会占用大量IO带宽,而系统卡顿的感知源头正是这个过程,而不是swap本身数值有多大。当物理内存接近满载,即使swap写的数据量不大,每次换页带来的瞬时高延迟写入也会让整个磁盘排队,拖慢其他IO请求。这种情况用iostat看会看到磁盘util接近100%,await很高。

解决办法是给系统更多内存,或者减少进程内存占用。如果条件不允许,可以调低swappiness让内核尽量少碰swap,同时清理掉明显的多余进程。

5.4 开机提示swap挂载失败或系统卡在等待swap设备

这种问题多半是/etc/fstab里写了一个不存在的swap设备。比如你用swap文件方式删除了旧的/swapfile,但fstab里还留着一行/swapfile none swap sw 0 0。开机时systemd会一直等这个设备,表现为系统启动卡很久最后进入emergency mode。

定位方法很简单,开机时看到等待设备提示,输入root密码进入shell,然后编辑/etc/fstab把那一行删掉或者注释掉。注释用#号开头,保存后重启即可。

更稳妥的做法是每次创建或删除swap之后都检查一遍fstab。删除swap文件时一定记得同时删fstab对应行。这是我在给一台机器做swap缩容时踩过的坑,那次直接让我从一个分区环境折腾到了物理机前。

5.5 swap位置引起的软件兼容问题

还有一类问题容易被忽略,就是某些软件或者内核特性要求swap空间存在,否则会运行异常。比如systemd的某些内存管理单元、低内存杀手、休眠功能都会检查swap是否存在。容器运行时在cgroup v2环境下如果检测到swap限制,也可能报warning。

很多加固脚本会建议“关闭swap”,但这个建议在容器化时代值得重新审视。Kubernetes节点上,swapoff是历史版本的常见要求,因为早期kubelet对swap支持不完善。但新版本的Kubernetes已经能在节点上支持swap,只要合理配置。所以,“关掉swap”这个建议不是放之四海而皆准,总的原则是根据业务形态选择策略,而不是一刀切。

6. swap和现代技术的配合方式

聊完基本的swap操作,再讲几个稍微进阶的方向。

内存压缩技术zram跟zswap不一样,它是在内存里拿出一块固定大小的区域,经过压缩后作为一个块设备使用,然后把这个块设备格式化成swap。这样比起不压缩的原始内存,能容纳更多匿名页。在内存很小的设备上,zram带来的效果很明显。适合嵌入式设备、Android、瘦客户端。

传统swap磁盘换页在SSD时代也有一个优势:让物理内存的分配请求能暂时落在高性能NVMe磁盘上。虽然延迟不低,但至少进程保住了。在实际运维中,对一个32GB内存的裸金属服务器,SSD上的这个小swap时常能用特殊方式避免OOM带来的灾难。

Kubernetes环境里,如果节点内存压力大,kubelet会基于eviction硬阈值驱逐Pod,而不是依赖swap。但如果节点同时还有dramatic swap交换,Pod的CPU调度就可能因为IO等待被拉高,这种异常更隐蔽。给K8s节点配swap时,我建议要么不配,要配就只在SSD上,而且把swappiness调低到10以下,让swap只是兜底用的。

我个人在实际操作中比较倾向的策略是这样的:任何一台Linux机器,哪怕内存再大,我也会留一个2GB的swap兜底。它平时基本用不上,但真出现内存漏的时候,能为我争取到登录机器排查的时间,这比被OOM杀掉进程然后猜测原因要舒服得多。

最后再分享一个小技巧:如果想临时模拟内存压力测试swap效果,可以写个一次性脚本占内存。

stress-ng --vm 2 --vm-bytes 12G --timeout 60s

没有stress-ng的发行版先装一下。跑脚本的同时开另一个终端用vmstat 1观察si和so的变化,你对swap的感性认识会一下子牢靠起来,远比只看文档来得直观。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 1:30:25

EtherCAT从站硬件控制器设计实战:STM32+LAN9252方案与调试指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:28:04

状态模式实战:订单状态流转、if-else重构与并发持久化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:27:52

Win7蓝屏排查实战:配置转储文件并用WinDbg定位驱动故障

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:27:51

STM32开发必备:国内高效资源平台与参考方案全指南

1. 为什么“找参考方案”比“从零写代码”更值得花时间STM32 这颗芯片在国内嵌入式圈子的地位,用一句话概括就是:你绕不开它。从高校实验室的毕业设计,到工业现场的电机控制板,再到消费电子里的智能台灯、鱼缸控制器,S…

作者头像 李华
网站建设 2026/9/30 1:27:49

BMS上车前必须经历哪些测试?从功能验证到失效安全的完整流程

一块BMS板子从画好PCB、打好样、焊完器件,到真正装进电池包上车,中间隔着的不只是几次“测试通过”的邮件,而是一整套能把设计逼出原形的验证流程。我入行做电池管理系统那会儿,最天真的想法就是“板子能跑、采样准、通信通”就能…

作者头像 李华
网站建设 2026/9/30 1:27:22

NAT本质是户籍管理而非地址翻译:会话表驱动的排障方法论

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华