1. Swap被占满的常见场景:什么时候需要动手
先说结论:swap本身不是洪水猛兽,它只是内存和磁盘之间的次级缓存层。Linux内核在物理内存(RAM)不足时,会把一部分不常用的内存页挪到磁盘上的swap分区或swap文件中,腾出RAM给活跃进程用。这个机制本身没有任何问题,问题出在两种操作场景下,你会特别想“清一清”它。
第一种场景是服务进程内存吃紧后把swap撑满。比如你跑了个Java应用、MySQL实例或者编译大型C++项目,内存峰值冲上去之后,swap被大量写入。等进程退出、内存释放后,swap上的那些页就成了“死数据”,它不会自动被换回RAM,只会一直躺在磁盘上,直到被再次访问或系统重启。这时候你执行free -h,会看到Swap这一行的used值仍然很高,明明物理内存明明很充足,swap却毫无必要地占据着。
第二种场景是系统监控告警触发,强迫症发作。我接过不少朋友的求助,他们的云服务器就2GB内存,跑了个Node服务经常触发swap使用率告警,他们第一反应是“把swap清掉”。这里要泼一盆冷水:如果物理内存长期不足,你去清swap,系统会立刻重新把数据换入RAM,然后很快再次换出,造成频繁换页(thrashing),性能反而更差。真正该做的是升级内存、优化进程参数或者显式限制某些服务的内存用量。清swap只适用于“流量高峰已经过去、当前物理内存余量充足”的窗口期。
所以,适合动手清swap的前提条件很简单:物理内存(可用部分)大于swap当前使用量,系统负载正常,磁盘没有告警。满足这三个条件后再往下操作,你会很从容。
2. 释放swap的核心原理:为什么不能直接“卸载”
很多新手第一次搜到“释放swap”,看到的第一条建议是swapoff -a——把swap整体关掉再打开。这样做确实能把swap里的数据全部换回物理内存,代价是:swapoff会在操作期间尝试把swap上的所有页都换回RAM,如果RAM不够大,内核会进入内存回收流程,甚至触发OOM Killer杀进程。所以在物理内存不够的时候执行swapoff -a,是会死人的。
更稳妥的思路有两个。一个是用sysctl vm.swappiness调整内核的换页偏好,减少未来swap写入;另一个是上面说的“卸下再挂载”,但要先确认内存余量。
另外很多资料里提到的swapoff /dev/sda2 && swapon /dev/sda2,本质上就是让swap设备短暂“消失”再“重现”。只要内存足够,内核会把swap上的页全部换入RAM,然后重新挂载swap设备,此时swap的used基本归零。这套操作在RHEL系、Debian系和Ubuntu上都是通用的,唯一的差别是swap设备的命名方式和是否需要修改/etc/fstab。
2.1 区分swap分区与swap文件
在动手前,要先搞清楚机器的swap到底是什么形式。多数传统服务器用的是独立分区,比如/dev/sda2;但云主机、容器环境里越来越常见的是swap文件,比如/swapfile。两者在释放操作上几乎一样,但查看命令略有差别。
查看当前swap配置:
swapon --show输出大致长这样:
NAME TYPE SIZE USED PRIO /dev/sda2 partition 2G 1.2G -2如果是文件型swap,NAME一栏会显示文件路径。还有一种是ZRAM,属于内存压缩型swap,它的“释放”逻辑和磁盘swap完全不同,不能简单用swapoff处理——ZRAM的used高不代表磁盘空间被占用,所以也不需要刻意释放。这里先提醒一下,后面再展开。
2.2 swapiness参数不是用来“清空”的
vm.swappiness控制的是内核“多积极”地使用swap,取值范围0到100。数值越低,内核越倾向于保留物理内存中的匿名页,更少写入swap;数值越高,越愿意提前换出页面。但它是一个偏好阈值,不是你执行sysctl之后swap就被清空的开关。把这个参数调低,可以降低未来swap被塞满的概率,但对已存在的swap数据没有任何清理作用。
想要真正释放已占用的swap,核心操作还是swapoff这一条路,swappiness最多只能帮你“治未病”。
3. 安全释放swap的完整操作步骤
下面这套流程,我自己的生产环境里跑过很多次,也帮好几个读者远程解决过告警。核心原则就一句话:先确认内存余量,再执行swapoff,最后顺手调优防复发。
3.1 第一步:检查内存与swap现状
先看整体水位:
free -h看Swap行的used值。再看Mem行的available值——available才是内核认为“真正可以分配出去而不触发swap”的内存量,比free列里的数值更实用。如果available大于swap used的值,可以继续;如果相差不到10%,先去排查是哪个进程在吃内存:
ps aux --sort=-%mem | head -20这一步极其重要。我见过太多人没看内存就直接swapoff -a,结果OOM Killer把数据库进程杀了,教训惨痛。
3.2 第二步:执行swapoff之前先锁定大内存进程
如果你在生产环境操作,我建议多做一步:查一下当前所有进程的swap占用情况,确认没有哪个进程异常占用了大量swap。Linux没有直接的“查看进程swap占用”命令,但可以通过两个途径间接判断:
一是检查/proc下的smaps文件:
grep VmSwap /proc/*/status 2>/dev/null | sort -k2 -n -r | head -10二是用smem工具,如果系统里没有,先安装:
# Debian/Ubuntu apt install smem # RHEL/CentOS yum install smem smem -rs swap | head -20这个工具看Swap列很直观。如果发现某个进程独占了几百MB甚至上GB的swap,而且它本身就是那个“高峰流量进程”,那就好办了——等它自然释放即可;如果它一直在长驻且swap占用线性上涨,说明可能存在内存泄漏,先处理泄漏再清swap才有意义。
3.3 第三步:执行swapoff再swapon
确认内存余量充足、没有异常进程后,直接执行:
sudo swapoff -a sudo swapon -a这两条命令如果成功,free -h里的Swap used应该归零或者接近归零。为什么接近归零?因为swapon -a挂载后,内核会按当前工作负载重新建立一小部分交换页,这属于正常现象。
如果你只想释放某一个swap设备,不碰其他的,可以指定路径:
sudo swapoff /dev/sda2 sudo swapon /dev/sda2如果是swap文件:
sudo swapoff /swapfile sudo swapon /swapfile这里有个细节:swapon -a会按/etc/fstab配置自动挂载所有标记为swap的设备,而手动指定路径则只处理单个设备。日常单机操作直接用-a更省事。
3.4 第四步:验证释放结果
free -h swapon --showswap used如果还剩一小部分,可以再等几秒重新看。内核换页是异步的,可能刚执行完显示还有残留,过一会儿就归零了。另外也可以用vmstat 1 3连续采样3秒看si和so两列的值——也就是swap in / swap out的每秒页数。如果释放后这两列数值都非常低,说明系统没有在持续疯狂换页,状态是健康的。
4. 三种swap类型下的不同“释放”姿势
上面说的流程,针对的是最标准的交换分区和交换文件。实际生产里还有几种变体,处理方式不同,我分别说一下。
4.1 swap分区与swap文件:统一采用上节流程
这两种在释放方式上完全一致,区别只在于持久化配置。分区型swap通常写在/etc/fstab里,格式大概是:
/dev/sda2 none swap sw 0 0文件型swap则在挂载时指定了loop设备:
/swapfile none swap sw 0 0如果你执行swapoff -a后不执行swapon -a,重启后会重新挂载,但当前登录会话里系统就没有swap了。所以在释放时我习惯成对执行,不要只执行一半。
这里还要多提醒一句:如果你之前用mkswap格式化过某个设备,但后来没写入/etc/fstab,那swapon -a不会重新挂载它。需要手动swapon。否则你执行的swapoff会让该设备保持未挂载状态,直到你手动挂载或重启。
4.2 ZRAM设备:不建议用常规swapoff
ZRAM的原理是用CPU压缩内存页面,再放入一块内存区域作为swap设备。它不涉及磁盘I/O,所以“占用swap空间”实际上是“占用了内存中的压缩块”。ZRAM的特点是:压缩率高的场景下,系统能“凭空”获得几倍的可用内存;但在你看到swapon --show时,它显示的是未压缩的逻辑大小,不是真实的物理内存占用。
对于ZRAM,如果你执行swapoff /dev/zram0再swapon /dev/zram0,确实能让它的used归零,但这会导致一个副作用:原本放在ZRAM里的压缩页被解压回RAM,内存的压力会瞬间增大,然后内核再决定是否重新压缩写入。这个过程又慢又消耗CPU,多数情况下得不偿失。
所以我的看法是:ZRAM场景下根本不追求used归零。如果ZRAM使用率过高,你应该关注的是总内存水位,而不是swap的used列。真要想重置ZRAM,在桌面Linux上可以通过重启服务或重启系统来完成,服务器场景我一般不推荐杀死进程强清。
4.3 swap文件新增或扩容的正确姿势
顺带讲一个相关但不同的话题——很多人发现自己swap不够用后,第一反应是释放,但我建议分情况处理。如果swap used长期超过50%,且服务对内存不敏感,直接新增或扩容swap文件可能更合理:
# 创建1G的swap文件 sudo fallocate -l 1G /swapfile_extra sudo chmod 600 /swapfile_extra sudo mkswap /swapfile_extra sudo swapon /swapfile_extra注意fallocate在某些文件系统(如旧版XFS)上可能会产生空洞,而swap文件不允许有空洞。稳妥做法是用dd:
sudo dd if=/dev/zero of=/swapfile_extra bs=1M count=1024 sudo chmod 600 /swapfile_extra sudo mkswap /swapfile_extra sudo swapon /swapfile_extra这两种方式产生的swap文件功能一样,但dd在兼容性上更保险。用dd创建1GB文件需要等待几十秒到几分钟,取决于磁盘速度,别把它当成故障。
5. 彻底“根治”:让swap不再轻易占满的调优思路
前面说过,释放swap只是治标。如果你频繁需要清理swap,说明系统存在“病根”。经过多次实践,我给读者建议的排查顺序通常是:
5.1 调整swappiness参数
先看当前值:
cat /proc/sys/vm/swappiness大多数服务器默认是60,这个数值意味着内核在内存接近满的时候会“比较积极”地换出页面。如果物理内存够用,可以调低到10甚至0,让内核更倾向于保留物理内存页。临时设置:
sudo sysctl vm.swappiness=10永久生效,写入/etc/sysctl.conf或/etc/sysctl.d/99-swap.conf:
vm.swappiness=10然后执行:
sudo sysctl -p注意,swappiness=0并不意味着禁用swap,它只表示内核在无强压力下不去换出匿名页。真正的swap禁用是把设备整个swapoff,这是两码事。
5.2 缩小swap min free的触发阈值
除了swappiness,还有一个参数容易被忽略:vm.min_free_kbytes。它表示内核在分配内存时保留的最小可用内存量。如果你的机器内存较大但系统会出现“明明内存没满,swap却开始被写”的现象,可以稍微调高这个值,让内存分配更早触发回收,减少swap的被动写入。
不过这个参数是双刃剑。调太高会增加内存回收和DMA失败的几率,一般不建议超过物理内存的1%以下。默认值通常由内核自动按内存大小设置,除非你明确知道问题所在,否则不要轻易动它。
5.3 定位占用swap最多的进程
结合上面的smem或/proc/*/status,找到占swap大头的进程。常见病根:
- Java应用:JVM堆内存设置过大会导致大量堆页被换出,建议把
-Xmx调小或调整GC策略,让堆对象尽量不进入swap。 - 数据库:MySQL/PostgreSQL的buffer pool会占用大量内存,如果机器内存不足,建议显式调低
innodb_buffer_pool_size或shared_buffers。 - 文件缓存:页面缓存(page cache)在内存压力下也会被回收,但你不需要专门清理它,因为内核会自动处理;如果page cache占比过高且影响业务,可以适当调整
vm.vfs_cache_pressure。
5.4 云服务器场景的特别提醒
云服务器上的swap往往位于云盘或本地盘上,I/O性能和物理内存差距悬殊。频繁换页(si/so数值过高)会导致应用卡顿,这时候与其“释放swap”,不如考虑:
- 增加内存规格;
- 重新设计进程的内存上限;
- 使用更小的swap空间,比如从8GB缩到2GB,减少系统“无痛换页”的空间。
我个人在云主机上维护的一款后台服务,就是从“8GB swap + swappiness=60”调整为“2GB swap + swappiness=10”之后,swap used再也没持续超标过。
6. 实操中容易踩坑的细节补充
最后记录几个我在实际环境中踩过和见过的坑,希望能帮你避开。
6.1 swapoff报“Cannot allocate memory”
如果你执行swapoff -a时终端直接提示无法分配内存,说明物理内存余量不够承载swap里的全部数据。这时候不要硬来,赶紧按Ctrl+C中断,改用下面这种渐进方式:
# 先不同时关闭所有swap,而是逐个清理 # 同时减小swap替换压力 sudo sysctl vm.drop_caches=3 # 谨慎使用,会清空page cache不过drop_caches只能释放文件缓存,无法直接释放匿名页。根本办法还是先杀掉吃内存的进程,或者等服务自然降峰后再操作。如果只是想降低swap used的数值,也可以考虑临时新增一块swap,等系统稳定后再清理旧的,思路类似“借道换乘”。
6.2 系统重启后swap问题复现
很多读者清完swap后发现重启后又满了。原因通常是:某个开机的服务一直在大量申请内存,直接吃满了RAM。此时你应该按第5节的方法排查进程,半途而废的“清swap”只会让你对系统状态误判。
6.3 容器环境下的swap陷阱
Docker默认允许容器使用宿主机的swap空间,所以在容器里跑Java服务,可能你们宿主机上看到swap used飙高,但容器里free看到的内存却很健康。排查时先确认你在哪一层看到的数据,再决定操作对象。docker stats可以看容器内存,宿主机上的swap状态还是得在宿主机上处理。如果你想让某个容器禁止使用swap,可以在docker run时加--memory-swap=0或设置--memory与--memory-swap相等来禁用swap扩展。
6.4 小心误删swap分区导致无法开机
操作不当可能导致系统重启后没有swap,但一般不影响开机。真正危险的操作是编辑/etc/fstab时写错了UUID或设备名,导致开机时swap挂载失败。所以如果你改了/etc/fstab,务必先执行:
sudo mount -a sudo swapon -a确认无报错再重启。不要随手删除/etc/fstab里swap相关的行而不做验证。
7. 我的建议:以“预防”心态看待swap
总结这么多年跟Linux内存打交道的经验,我对swap的态度是:它是系统的一道安全缓冲,不是一块需要时刻保持“干净”的区域。释放swap本身并不难,难的是判断何时该释放、释放后如何防止它再次被无意义占满。
如果你只是想看一眼系统内存和swap状态:
free -h && swapon --show如果swap used超标且物理内存余量充足,就按第3节的流程走一遍;如果回想一下流量峰值已经过去,但仍然频繁告警,就去查进程、调swappiness、审视容器配置。这套组合拳下来,你会有更清晰的认识。
我个人习惯是在每次大促或压测结束后做一次swap巡检,把free -h、smem、vmstat的输出存个快照,观察几个周期内的变化趋势,而不是一味追求“清零”。毕竟,Linux的内存管理远比表面看到的复杂,尊重内核的调度策略,比盲目干预有效得多。