
Linux 内存回收机制水位线、kswapd 与 Direct Reclaim源码基线Linux v6.18.9mm/page_alloc.c/mm/vmscan.c。现象内存明明还有free数 GB进程却偶发卡顿几百毫秒P99 翻倍。根因kswapd 回收跟不上分配速度水位跌破min后业务线程被迫在分配路径上亲自回收Direct Reclaim并触发内核节流。一、三条水位线谁来收债─────────────────── high ← kswapd 回收到这里就睡 ─────────────────── low ← 跌破这里唤醒 kswapd后台收债业务无感 ─────────────────── min ← 跌破这里分配线程自己收债Direct Reclaim卡顿水位区间行为业务感知高于 low快路径直接返回页框无low ~ min唤醒 kswapd 后台回收基本无低于 min当前线程进入慢路径亲自回收卡顿grep-A5Node 0/proc/zoneinfo|grep-Emin|low|high# 查看水位单位页二、Direct Reclaim 调用链v6.18.9alloc_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收不到 → OOM7.0 中shrink_zones()更名为shrink_one()其余不变。为什么慢——回收线程扫到不同页的代价页类型代价量级干净 Page Cache直接丢微秒脏页回写 I/O 节流毫秒起匿名页换出 swap/zram毫秒三级节流卡顿的真正来源撞见大量回写页 →reclaim_throttle(VMSCAN_THROTTLE_WRITEBACK)睡最长 100ms回收连续无进展 →VMSCAN_THROTTLE_NOPROGRESSmin 以下保护区耗尽 →throttle_direct_reclaim()睡等 kswapd 救回水位多线程同时分配会集体进 Direct Reclaim形成集体抖动。MGLRU6.12 起默认改变扫描方式但水位线、触发条件、计数器全部不变排查方法照用。三、观测与实锤grep-Eallocstall|pgscan_direct|pgscan_kswapd|pgsteal|throttle/proc/vmstat计数器含义信号pgscan_kswapd后台扫描量稳定 正常pgscan_direct前台扫描量持续增长 业务在收债allocstall_*进 Direct Reclaim 次数增长 卡顿元凶pgscan_direct_throttle睡等 kswapd 救援次数非零 最危险阶段drsnoop-bpfcc# 专抓 Direct Reclaim 延迟cat/proc/pressure/memory# full avg10 高 撞见回写perfstat-evmscan:mm_vmscan_direct_reclaim_begin-asleep10典型卡顿栈__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数字在涨说明你的业务线程正在替内核收债。