Linux 内存回收机制:水位线、kswapd 与 Direct Reclaim

Linux 内存回收机制:水位线、kswapd 与 Direct Reclaim

源码基线:Linux v6.18.9 (mm/page_alloc.c / mm/vmscan.c)。

现象 :内存明明还有(free 数 GB),进程却偶发卡顿几百毫秒,P99 翻倍。

根因 :kswapd 回收跟不上分配速度,水位跌破 min 后,业务线程被迫在分配路径上亲自回收(Direct Reclaim),并触发内核节流。

一、三条水位线:谁来收债

text 复制代码
─────────────────── high   ← kswapd 回收到这里就睡
─────────────────── low    ← 跌破这里唤醒 kswapd(后台收债,业务无感)
─────────────────── min    ← 跌破这里,分配线程自己收债(Direct Reclaim,卡顿)
水位区间 行为 业务感知
高于 low 快路径直接返回页框 无
low ~ min 唤醒 kswapd 后台回收 基本无
低于 min 当前线程进入慢路径亲自回收 卡顿
bash 复制代码
grep -A5 "Node 0" /proc/zoneinfo | grep -E "min|low|high"   # 查看水位(单位:页)

二、Direct Reclaim 调用链(v6.18.9)

text 复制代码
alloc_pages()
  └─ 快路径失败 → __alloc_pages_slowpath()          # 先 wake_all_kswapds(),
                                                    # 再按 ALLOC_WMARK_MIN 重试
  └─ __alloc_pages_direct_reclaim()                  # psi_memstall_enter()
  └─ __perform_reclaim()
  └─ try_to_free_pages()                             # ← 业务线程开始收债
  └─ do_try_to_free_pages() → shrink_zones() → shrink_node()
       (MGLRU 启用时走 lru_gen_shrink_node())
  └─ 收到页返回(几十~几百 ms);收不到 → OOM

7.0 中 shrink_zones() 更名为 shrink_one(),其余不变。

为什么慢------回收线程扫到不同页的代价:

页类型 代价 量级
干净 Page Cache 直接丢 微秒
脏页 回写 I/O + 节流 毫秒起
匿名页 换出 swap/zram 毫秒

三级节流(卡顿的真正来源):

  1. 撞见大量回写页 → reclaim_throttle(VMSCAN_THROTTLE_WRITEBACK),睡最长 100ms
  2. 回收连续无进展 → VMSCAN_THROTTLE_NOPROGRESS
  3. min 以下保护区耗尽 → throttle_direct_reclaim(),睡等 kswapd 救回水位

多线程同时分配会集体进 Direct Reclaim,形成"集体抖动"。

MGLRU(6.12 起默认)改变扫描方式,但水位线、触发条件、计数器全部不变,排查方法照用。

三、观测与实锤

bash 复制代码
grep -E "allocstall|pgscan_direct|pgscan_kswapd|pgsteal|throttle" /proc/vmstat
计数器 含义 信号
pgscan_kswapd 后台扫描量 稳定 = 正常
pgscan_direct 前台扫描量 持续增长 = 业务在收债
allocstall_* 进 Direct Reclaim 次数 增长 = 卡顿元凶
pgscan_direct_throttle 睡等 kswapd 救援次数 非零 = 最危险阶段
bash 复制代码
drsnoop-bpfcc                                  # 专抓 Direct Reclaim 延迟
cat /proc/pressure/memory                      # full avg10 高 = 撞见回写
perf stat -e 'vmscan:mm_vmscan_direct_reclaim_begin' -a sleep 10

典型卡顿栈:__alloc_pages_direct_reclaim → __perform_reclaim → try_to_free_pages → shrink_node → reclaim_throttle

四、缓解手段

手段 作用点 代价
调大 vm.min_free_kbytes 抬水位,给 kswapd 提前量 常驻内存变少
调大 vm.watermark_scale_factor 更早唤醒 kswapd 同上
降低 vm.swappiness 倾向回收文件页 匿名页压力大时 OOM 更早
业务削峰 / 内存池 避免集体进慢路径 改业务
cgroup 限额 压力隔离到容器 限额内仍会发生
查磁盘与 vm.dirty_* 降低回写节流命中 基础设施成本

排查顺序 :pgscan_direct 涨 → 确认 Direct Reclaim → pgscan_direct_throttle 涨 → 抬水位+查 kswapd → 否则查 Dirty/Writeback 与磁盘 → 再查 swap/zram。

能做 :隔一分钟跑一次 grep -E "allocstall|pgscan_direct" /proc/vmstat,数字在涨,说明你的业务线程正在替内核收债。

相关推荐
HideF8 小时前
Linux sudo
linux
FlightYe9 小时前
视界原理之3D视频(四):裸眼、眼镜和头显
android·linux·c++·3d·ffmpeg·音视频
ZhengEnCi9 小时前
GPT-6 Astra 爆改 Qwen 权重真有那么神吗:权重空间编程 0.016% 参数的真相
算法·架构
罗西的思考9 小时前
【轻量化复刻大模型 / KIMI K3】KDA(Kimi Delta Attention):并行
人工智能·算法·机器学习
蹊黎9 小时前
Linux 下用 C 语言实现进度条
linux·c语言·单片机
牛马鸡niumasi11 小时前
【Linux】C语言文件操作
linux
say_fall11 小时前
【Linux系统编程】Ext 文件系统:磁盘与存储、文件与目录、现代计算机究竟如何寻找文件
linux·运维·服务器·数据库·c++
夜不会漫长11 小时前
C++:红黑树
数据结构·算法
wacsii12 小时前
【Linux进程(一)】Linux 进程初探:操作系统如何管理进程?fork() 为什么返回两次?
linux·运维·服务器
AI+程序员在路上12 小时前
RV1126B 上支持的模型及全流程应用--从CNN及语音到 LLMVLM 与跨平台选型
linux·c语言·人工智能