Linux 内存回收策略解析

9次阅读

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

Linux 内存回收策略解析

内存回收什么时候真正开始?看三个水位线

linux 不是等内存用光才动手,而是靠 minlowhigh 三条水位线分级响应。它们存于每个内存区(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),必须先写回磁盘(经 pdflushwriteback 线程),才能释放
  • 匿名页(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,而非消除根源。

text=ZqhQzanResources