4. 内存水线:watermark 机制、min/low/high 水线含义、zone watermarks

内存水线(watermark)是 Linux 内核 页面回收触发与抑制 的核心阈值机制。每个内存管理区(zone)都维护三根水线:min、low、high,它们决定了 kswapd 何时唤醒、何时睡眠,以及直接回收(direct reclaim)是否介入。

4.1 三根水线的物理含义

水线以页面数(page)为单位,基于 zone 的 managed_pages(可管理页面数)计算得出。三根水线将 zone 的空闲页面划分为四个区域:

水线名称 位置关系 触发行为 回收压力等级
min 最低阈值 低于 min → 触发直接回收(direct reclaim),分配者自身阻塞回收 🔴 紧急
low 中间阈值 低于 low → 唤醒 kswapd 异步回收,分配者仍可尝试快速分配 🟡 预警
high 最高阈值 高于 high → kswapd 睡眠,回收停止 🟢 正常
关键理解: min 是系统最后的防线;low 是 kswapd 的唤醒线;high 是 kswapd 的睡眠线。三者之间的差值称为 水线间隙(watermark gap),决定了 kswapd 一次回收的工作量。

4.2 水线计算与 min_free_kbytes

水线的计算起点是内核参数 vm.min_free_kbytes,它直接决定了 min 水线 的全局基数。计算公式如下:

// 简化后的水线计算逻辑(mm/page_alloc.c)
// 1. 根据 min_free_kbytes 计算每个 zone 的 min 水线
zone->_watermark[WMARK_MIN] = min_free_kbytes * zone->managed_pages / total_managed_pages;

// 2. low 水线 = min + (min / 2)   (默认比例)
zone->_watermark[WMARK_LOW] = zone->_watermark[WMARK_MIN] + (zone->_watermark[WMARK_MIN] >> 1);

// 3. high 水线 = min + min       (默认比例)
zone->_watermark[WMARK_HIGH] = zone->_watermark[WMARK_MIN] + zone->_watermark[WMARK_MIN];
调优经验: 增大 vm.min_free_kbytes 会整体抬高所有 zone 的三根水线,使 kswapd 更早唤醒、更晚睡眠。适合延迟敏感型业务(如数据库、实时计算),但会减少可用内存总量。

4.3 水线与 kswapd 状态机

kswapd 的行为完全由水线驱动,其状态转换如下图所示:

kswapd 水线驱动状态机 kswapd 水线驱动状态机 睡眠态 free > high 唤醒态 low < free < high 回收态 free < low free 下降 free 继续下降 回收后 free 回升 free 回升至 high 状态转换说明: 1. 睡眠态 → 唤醒态:free 页面低于 low 水线,kswapd 被唤醒。 2. 唤醒态 → 回收态:free 页面低于 low 水线且持续下降,kswapd 开始扫描回收。 3. 回收态 → 唤醒态:回收后 free 回升至 low 以上,kswapd 停止回收但保持唤醒。 4. 唤醒态 → 睡眠态:free 回升至 high 以上,kswapd 完全睡眠。

4.4 水线对分配路径的影响

内存分配器(allocator)在分配页面时,会根据当前 zone 的空闲页面数选择不同的分配路径:

  • free > high:快速路径(fast path),直接从 per-CPU 页框缓存(pcp) 分配,无锁或低锁开销。
  • low < free < high:慢速路径(slow path),尝试从伙伴系统分配,同时检查是否需要唤醒 kswapd。
  • min < free < low:kswapd 已唤醒,分配者可能等待 kswapd 回收,或尝试 ALLOC_HARDER 标志。
  • free < min:直接回收(direct reclaim),分配者自身调用 try_to_free_pages(),这是最昂贵的路径。
性能陷阱: 频繁进入 direct reclaim 是内存压力的明确信号。如果 /proc/vmstat 中的 pgsteal_direct 持续增长,说明水线设置过低或 kswapd 回收速度跟不上分配速度,应适当提高 min_free_kbytes。

4.5 查看当前水线

通过 /proc/zoneinfo 可以查看每个 zone 的实时水线值:

# 查看所有 zone 的水线信息
cat /proc/zoneinfo | grep -E "Node|zone|watermark"

# 输出示例(DMA32 zone):
Node 0, zone   DMA32
  pages free     12345
  min      5678
  low      8517
  high     11356
  spanned  1048576
  present  1048576
  managed  1012345

各字段含义:

  • free:当前空闲页面数。
  • min / low / high:三根水线值。
  • spanned:zone 覆盖的总页面数(含空洞)。
  • present:zone 实际存在的页面数。
  • managed:内核可管理的页面数(present 减去预留页),水线基于此值计算。

4.6 水线调优参数

参数 默认值 调优方向 适用场景
vm.min_free_kbytes 通常为 1%~2% 物理内存 增大 → 提前回收,减少 direct reclaim 数据库、高频交易、实时系统
vm.watermark_scale_factor 10(0.1%) 增大 → 扩大 low-high 间隙,kswapd 一次回收更多 大内存机器、批量回收场景
vm.watermark_boost_factor 15000(150%) 增大 → 回收后临时抬高水线,抑制突发分配 内存抖动频繁的业务
调优公式: low = min + (min × watermark_scale_factor / 10000)
high = min + 2 × (min × watermark_scale_factor / 10000)
默认 watermark_scale_factor = 10,即 low = min + 0.1% × min,high = min + 0.2% × min。

4.7 水线架构总览

下图展示了水线在内存分配与回收路径中的核心位置:

内存水线架构总览 内存水线架构总览 内存分配请求 水线判断 free > high 快速路径(pcp) low < free < high 慢速路径(唤醒 kswapd) free < low 直接回收(direct reclaim) 回收页面 → free 回升 反馈循环 水线是内存分配路径的“交通信号灯”:绿色(high)畅通无阻,黄色(low)预警减速,红色(min)紧急刹车。 调优的核心目标是让系统尽可能停留在绿色区域,避免频繁进入红色区域。

4.8 实战:水线异常排查

当系统出现内存压力时,可以通过以下命令快速定位水线问题:

# 1. 查看全局水线配置
sysctl vm.min_free_kbytes vm.watermark_scale_factor vm.watermark_boost_factor

# 2. 查看各 zone 水线与实际 free 的差距
cat /proc/zoneinfo | awk '
  /Node/ {node=$2}
  /zone/ {zone=$2}
  /free/ {free=$2}
  /min/ {min=$2; printf "%-10s %-10s free=%-8d min=%-8d diff=%-8d\n", node, zone, free, min, free-min}
'

# 3. 观察 direct reclaim 频率
grep -E "pgsteal_direct|pgscan_direct" /proc/vmstat
快速判断: 如果 free - min 长期小于 low - min(即 free 在 low 以下徘徊),说明 kswapd 回收能力不足,应增大 min_free_kbytes 或优化回收效率。