内存回收由min、low、high三水位线分级触发:low被击穿时kswapd异步回收,min被击穿时进程阻塞并直接回收,持续低于min则触发OOM Killer;真实压力需查/proc/zoneinfo中pages_low与pages_free差值。

内存回收什么时候真正开始?看三个水位线
Linux 不是等内存用光才动手,而是靠 min、low、high 三条水位线分级响应。它们存于每个内存区(zone)的 watermark[] 数组中,关系固定:min 。
-
low被击穿 →kswapd守护进程被唤醒,异步扫描 inactive 链表,悄悄回收页面,不卡应用 -
min被击穿 → 当前分配内存的进程被阻塞,触发 直接回收(Direct Reclaim),同步清理,延迟明显可感 - 持续跌破
min且无法缓解 → 最终触发OOM Killer,按oom_score_adj杀进程保系统
别只盯着 free -h 的 “available”,它只是估算;真实压力信号藏在 /proc/zoneinfo 里——查 pages_low 和当前 pages_free 的差值,才是判断是否已进入回收临界的真实依据。
匿名页 vs 文件页:回收逻辑完全不同
回收不是“一锅端”,内核对两类页区别对待:
-
文件页(File Pages):比如读取过的
/etc/passwd缓存在内存里。若未修改(clean),直接丢弃,下次访问再从磁盘读;若已修改(dirty),必须先写回磁盘(经pdflush或writeback线程),才能释放 -
匿名页(Anonymous Pages):进程堆、栈、
mmap(MAP_ANONYMOUS)分配的内存。无磁盘备份,只能写入swap分区(或压缩后留在内存,见下条),否则就只能丢弃——但丢弃前得确认它没被写过(PageDirty为 false)
这意味着:纯计算型服务(如 Java 应用堆大但文件缓存少)更容易触达 swap;而高 IO 服务(如数据库缓存大量脏页)可能卡在回写队列,表现为 wa% 持续偏高,pgpgout 却上不去。
swappiness 是开关,不是油门
vm.swappiness 控制的是「匿名页」和「文件页」在回收时的相对优先级,不是“越小越不 swap”。它的作用点在 LRU 链表扫描阶段:
- 值为
0:仅在内存极度紧张(min水位以下)且文件页已基本清空时,才考虑 swap 匿名页 - 值为
60(默认):匿名页和文件页按比例混合回收,倾向保留更多文件缓存 - 值为
100:尽可能把匿名页换出,哪怕文件页还很充裕
常见误区:给数据库服务器设 swappiness=1 就能杜绝 swap?错。只要 kswapd 扫到足够多不活跃匿名页,且文件页已回收殆尽,它仍会 swap。真正有效的是配合 vm.vfs_cache_pressure=50(减少 dentry/inode 缓存回收压力)+ 限制进程 memcg 内存上限,把压力挡在 zone 外。
压缩 swap(zswap)不是万能加速器
启用 zswap(需内核开启 CONFIG_ZSWAP)后,内核会把准备 swap 的匿名页先压缩,暂存于内存中的 zpool,只有当压缩池满或页面太久未访问,才真正落盘。
- 优势:大幅减少磁盘 I/O,尤其对 SSD 寿命友好;对突发性内存峰值响应更快
- 代价:CPU 压缩/解压开销(实测约增加 2–5% sys CPU);压缩失败的页面仍要走传统 swap 路径
- 坑点:
zswap默认使用lzo算法,压缩率低但快;换成lz4可提升压缩率,但需确认内核支持且不引入额外延迟
它解决的是「swap 频繁导致 IO 瓶颈」的问题,不是「内存根本不够」的问题。如果你的 SwapTotal 已接近耗尽,zswap 只是延缓 OOM,而非消除根源。








