1. 内存回收概述:物理内存紧张时的系统行为

当 Linux 系统物理内存不足时,内核必须主动介入,通过一系列策略回收部分页面以维持系统运行。本章首先介绍内存紧张时的系统行为、OOM Killer 的触发条件,以及页面回收的三种主要途径:直接回收、后台回收和 kswapd。

1.1 物理内存紧张时的系统行为

物理内存的分配与回收是一个动态平衡过程。当内存分配请求无法被满足时,内核会逐步升级响应策略:

阶段 触发条件 系统行为
正常分配 空闲内存充足 直接从伙伴系统分配物理页,无延迟
轻度紧张 空闲内存低于 pages_low 阈值 唤醒 kswapd 内核线程,异步回收页面
中度紧张 空闲内存低于 pages_min 阈值 分配路径进入直接回收(direct reclaim),同步等待
严重紧张 直接回收仍无法满足分配 触发 OOM Killer,选择进程并强制终止
关键阈值:内核通过 /proc/sys/vm/min_free_kbytes 控制 pages_min 的大小,pages_low 通常为 pages_min 的 2 倍,pages_high 为 pages_min 的 3 倍。这些阈值决定了 kswapd 的唤醒频率和直接回收的触发时机。

1.2 OOM Killer 触发条件

OOM(Out-Of-Memory)Killer 是内核在极端内存压力下的最后手段。其触发条件非常严格:

  • 分配标志位检查:只有分配请求携带 __GFP_FS 和 __GFP_IO 标志时,才允许进入 OOM 路径。
  • 直接回收失败:内核在尝试直接回收、压缩内存、等待 kswapd 后,仍无法获得所需页面。
  • 无可用 kill 候选:如果所有进程都处于不可杀状态(如内核线程、正在执行关键操作的进程),OOM Killer 会放弃并触发内核 panic。
  • oom_adj 与 oom_score:每个进程的 /proc/pid/oom_adj 可调整其被选中的概率,oom_score 由内核根据内存占用、运行时间、进程优先级等计算得出。
注意:OOM Killer 并非“随机杀人”。内核使用 badness() 函数对进程打分,分数最高的进程被选中。root 进程、init 进程、以及设置了 OOM_DISABLE 的进程不会被 kill。

1.3 页面回收的三种主要途径

Linux 内核通过三种途径回收物理页面,它们在不同压力等级下协同工作:

  1. 直接回收(Direct Reclaim):分配路径同步执行,调用 try_to_free_pages(),扫描 LRU 链表并回收页面。调用者会阻塞等待。
  2. 后台回收(Background Reclaim):由 kswapd 内核线程异步执行,在空闲内存低于 pages_low 时唤醒,回收到 pages_high 后休眠。
  3. KSwapd:每个内存节点(NUMA node)拥有独立的 kswapd 线程,负责该节点的页面换出和匿名页交换。
核心区别:直接回收是同步阻塞的,后台回收是异步非阻塞的。直接回收发生在分配路径中,而 kswapd 在后台独立运行。当系统内存压力持续升高时,直接回收的比例会显著增加,这是系统性能下降的典型信号。

1.4 内存回收整体流程

下图展示了从内存分配请求到页面回收的完整决策路径:

内存回收整体决策流程 内存分配请求 空闲内存 ≥ pages_low? 正常分配(快速路径) 是 唤醒 kswapd 后台回收 否 空闲内存 ≥ pages_min? 分配成功 是 直接回收(同步阻塞) 否 回收成功? 是 触发 OOM Killer 否 说明: • pages_low = 2 × pages_min • pages_high = 3 × pages_min • kswapd 回收至 pages_high 后休眠

1.5 三种回收途径的对比

特性 直接回收 后台回收(kswapd) OOM Killer
触发时机 分配路径中,空闲内存 < pages_min 空闲内存 < pages_low 直接回收失败且无可杀进程
同步/异步 同步阻塞 异步非阻塞 同步(进程被强制终止)
回收目标 满足当前分配请求 恢复到 pages_high 阈值 释放进程所有内存
性能影响 高(调用者等待) 低(后台执行) 极高(进程终止)
可观测性 /proc/vmstat 中 pgscan_direct /proc/vmstat 中 pgscan_kswapd 系统日志 dmesg 记录

1.6 内存回收的观测指标

系统管理员可以通过以下指标判断当前内存回收状态:

  • /proc/meminfo:查看 MemFree、MemAvailable、SwapTotal、SwapFree 等基础信息。
  • /proc/vmstat:关注 pgscan_direct(直接回收扫描页数)、pgscan_kswapd(kswapd 扫描页数)、oom_kill(OOM 触发次数)。
  • /proc/zoneinfo:查看每个内存区域(zone)的 pages_min、pages_low、pages_high 阈值。
  • dmesg:OOM Killer 触发时会输出详细的进程打分信息和内存状态。
实践建议:当 pgscan_direct 持续增长且占比超过 pgscan_kswapd 时,说明系统内存压力已经很大,直接回收频繁发生,应考虑增加物理内存或调整 vm.min_free_kbytes 参数。

1.7 小结

本章介绍了 Linux 内核内存回收的宏观框架:物理内存紧张时,系统从正常分配逐步过渡到 kswapd 后台回收、直接回收,最终可能触发 OOM Killer。三种回收途径各有不同的触发条件、同步方式和性能影响。理解这些基础概念是后续深入学习 LRU 链表和页面置换机制的基石。