前言“内存回收”Reclaim是指寻找可以从当前使用者手中收回并投入到系统中更好用途的内存的任务它是操作系统内存管理全局图景中的核心部分。长期以来Linux 内核一直依赖“传统 LRU”算法来维系内存的生存周期。然而随着硬件规模的爆发传统 LRU 逐渐不堪重负。为此内核引入了全新的“多代架构 LRUMGLRU”。但这项本意为了提供更好回收实现的创新如今却让内核代码的局面变得更加复杂。在 2026 年 Linux 存储、文件系统、内存管理和 BPF 峰会LSFMMBPF上内存管理专题中足足有三个小节聚焦于 MGLRU旨在更全面地整合它、提升其性能并解决在 Android 系统中遇到的一些现实问题。本文将结合本次峰会的最新讨论为您全面解构这场正在 Linux 内核深处上演的内存管理技术变革。一、 传统 LRU双回路的“活跃”与“非活跃”传统的 LRU 算法在 Linux 中并非一个简单的线性链表。为了防止“预读垃圾”例如某些文件仅被读取一次随后便不再使用污染链表Linux 设计了一套双回路机制。1. 核心架构四大链表传统 LRU 将内存页按状态和性质划分核心维护着 4 个链表Active Anon / Inactive Anon活跃/非活跃 匿名页如进程堆栈、动态分配的数据Active File / Inactive File活跃/非活跃 文件页如页面缓存 Page Cache新产生的内存页通常先进入Inactive非活跃链表。只有当它在非活跃链表中被再次访问时内核才会将其升级Promote到Active活跃链表。2. 传统 LRU 的致命痛点随着现代服务器内存迈入 TB 级、智能手机内存迈入 16GB传统 LRU 的固有缺陷开始暴露粒度太粗只有两代页面非黑即白要么“活跃”要么“非活跃”。操作系统无法在庞大的物理内存中精准识别出哪些是“极热数据”哪些是“次热数据”。反向映射rmap开销巨大为了判断一个页面最近是否被访问传统 LRU 必须通过反向映射Reverse Mapping去遍历所有引用该页面的进程页表项PTE检查其中的Accessed位。当大量进程共享内存或遭遇高并发时这种遍历会引发可怕的锁竞争导致系统在内存紧张时 CPU 飙升至 100%引发系统抖动Thrashing甚至卡死。文件页与匿名页平衡困难内核依赖偏向静态的swappiness机制和一些特殊的启发式算法来决定回收哪类内存。这种“盲人摸象”的算法经常产生误判例如错误地驱逐了频繁使用的文件缓存导致系统频繁发生磁盘 I/O 停顿。二、 MGLRU精细化的“多代同堂”为了彻底解决传统 LRU 的历史包袱Google 工程师主导设计了MGLRU多代架构 LRU并于近年正式合入 Linux 内核主线。1. 核心原理划分“世代”MGLRU 颠覆了传统 LRU 的二元划分法它引入了类似于人类社会的“世代Generations”概念默认通常为 4 个世代未来可扩展至更多。最新一代Youngest Generation存放刚刚被访问过、热得发烫的数据。最老一代Oldest Generation存放长期无人问津的“冷数据”。内存回收时内核只从“最老一代”中挑选页面驱逐。随着时间的推移页面会在世代之间平滑地“老化”或因再次访问而“重获新生”。2. 颠覆性的技术改进MGLRU 不仅仅是增加了链表数量更在底层技术上进行了大刀阔斧的革新页表扫描Page-table ScanningMGLRU 放弃了高开销的反向映射。它采用“正向线性扫描”进程页表的方式来收集页面的访问信息这种方式对 CPU 缓存Cache极其友好几乎没有锁竞争。布隆过滤器Bloom Filter为了避免无脑扫描那些空闲或根本不活跃的页表区域MGLRU 引入了布隆过滤器。如果过滤器判断某个内存区域近期没有访问内核就会直接跳过扫描极大降低了 CPU 损耗。宋楷瑞在 2026 峰会上指出实验证明该过滤器对聚焦内存热点区域“非常有帮助”。PID 控制器动态平滑在平衡文件页和匿名页的回收比例时MGLRU 引入了现代工业控制中的 PID比例-积分-微分控制器动态、平滑地根据近期重分配refault的代价来调整比例避免了系统表现大起大落。三、 传统 LRU 与 MGLRU 的直观对比特性维度传统 LRU (Traditional LRU)MGLRU (Multi-Gen LRU)世代粒度仅 2 代 (Active / Inactive)多个世代 (通常 4 代宋楷瑞正在研究扩展至 64 代)活跃度检测反向映射遍历 (rmap walk)高并发下锁竞争严重页表正向扫描 (Page-table scan) 布隆过滤器高负载表现容易触发锁饱和导致 CPU 飙升、系统“抖动”锁粒度极低内存超发时系统依然能保持响应水位线控制持续驱逐页面直到达到低水位线Lower Watermark采用更复杂的负载均衡算法回收速度与 OOM触发 OOM Killer 的速度较慢容易导致长时间卡顿触发 OOM 速度更快但这被视作防止系统抖动的优秀特性特殊保护机制避免让可执行页面降级Protect Executable Pages优先考虑通过mmap()映射的文件页重分配页面处理将快速重分配的页面直接移至活跃列表Active List仅仅标记那些快速重分配的页面核心应用场景传统小内存架构、老旧/小众嵌入式设备内核必须支持的“不重要架构”现代海量内存服务器、云原生高并发环境、高超发布局的移动端如 Android四、 2026 峰会焦点之一统一回收代码的博弈正如 Shakeel Butt 在峰会上尖锐地指出“内存回收在内核中是一团糟。”目前这两个完全独立的淘汰算法共同挤在长达 8000 多行的mm/vmscan.c文件中。1. “一地鸡毛”的现状与挑战双重维护开销mm/vmscan.c中有 40% 是 MGLRU 特有的代码它们与传统路径功能高度重复。每一次漏洞修复、优化或新特性都必须做两次否则就只能对一半的用户生效。这被社区普遍认为是“不可持续的”。词汇与算法的割裂Emil Tsalapatis 指出两套实现有很多共同特性却使用了不同的词汇。例如重分配距离refault distance计算方式截然不同原因不明。传统 LRU 的实现“久经沙场”未来 MGLRU 可能会切换到传统路径。指标与计数器维护着不同的统计计数器如果想共享代码必须先统一计数器。平衡启发式策略传统 LRU 靠swappiness驱动MGLRU 靠 PID 控制器。目前尚不清楚 PID 控制器是否真的带来了显著益处。/proc/meminfo 指标波动MGLRU 对活跃/非活跃指标管理不理想数据容易出现“跳跃”因为它将最年轻的两代视为活跃世代老化时会导致数据剧烈波动。2. 社区的统一路线图为了终结这种混乱Shakeel Butt 提出了一个包含四个步骤的高层规划文件拆分将这两套代码库拆分到各自独立的文件中而不是挤在一起。定义工作负载收集并定义一套能够揭示算法优缺点的重要工作负载场景用于评估回收补丁防止性能退化。寻找共同点识别两套实现之间的共同特性与可移植的启发式策略。对比与抉择深入对比每个特性的具体实现在两个选项之间做出取舍例如尝试将页表扫描和反向映射在两个算法间对调并使实现通用化。辩论火花拆分文件的计划在峰会上引发了激烈的讨论。Vlastimil Babka 担心分离文件会使统一任务复杂化也有人担心搞乱 Git 提交历史不过 Johannes Weiner 反驳称 MGLRU 反正也没多少 Git 历史。Chris Li 担心大范围的代码重构会与宋楷瑞正在进行的许多优秀 MGLRU 改进补丁如减少页面标志位、写回节流等产生严重的冲突。最终Johannes Weiner 提出了折中方案以宋楷瑞目前已经解决了很多指标和统一问题的补丁工作如 MGLRU-FG 补丁集作为起点合并后再进行文件拆分。内核开发大牛 Lorenzo Stoakes 和 John Hubbard 达成一致虽然短期内可能因为未知原因需要同时保留两者但长远的终极目标是内核中只保留一个融合了两大架构优点的、唯一的回收实现。五、 2026 峰会焦点之二宋楷瑞的 MGLRU 进化蓝图作为 MGLRU 领域的活跃核心宋楷瑞主持了关于如何让 MGLRU“更好、更聪明”的专题。MGLRU 已经被许多主流发行版采用证明了其通用价值但它依然不是完美的。1. 正在解决的缺陷写回节流Writeback Throttling过去 MGLRU 缺失针对控制组cgroup的写回节流目前正在修复但全局写回节流依然缺失。页面缓存Page Cache退化MGLRU 在处理拥有大量匿名页的工作负载时表现极其惊艳但在极度依赖页面缓存的工作负载下可能会出现性能退化。宋楷瑞正在主导的MGLRU-FG补丁集旨在改进工作集检测大幅提升页面缓存的保护力度。页面标志位Page-flags危机页面标志位在内核中极其稀缺。MGLRU 此前占用了 4 位导致其很难在标志位更少的 32 位老旧系统上运行。MGLRU-FG 系列成功将使用量从 4 位减少到了 3 位宋楷瑞表示可以进一步压榨到 2 位虽然会有轻微性能影响以确保它能运行在“所有我们不在乎的架构老旧架构”上。内存读取模型误判MGLRU 过去错误地认为应用程序期望内存读取绝不发生停顿从而对因缺页异常创建的页帧进行过快激活。但这并不符合通过mmap()映射的文件读取模型。目前 Barry Song 提出了补丁进行修复而宋楷瑞倾向于直接移除这种行为。2. 前沿实验与 BPF 扩展在未来规划中宋楷瑞展示了几个激进的想法传统 LRU 兼容模式通过将 MGLRU 的世代和层级直接缩减到 2 个让其直接模拟传统 LRU 的行为。64 代支持通过减少相关的页面标志位使用未来让 MGLRU 支持高达 64 个世代虽然 Matthew Wilcox 询问 64 代能好多少时宋坦言并无确切答案但实验成本极低。BPF 强力介入引入 BPFBerkeley Packet Filter挂钩。允许管理员编写 BPF 程序动态决定发生缺页的页帧应该放入哪一代或者在页面被访问时将其移至特定的世代。这将允许系统管理员完全自定义内核的内存回收策略不过会场有听众暗示这可能有些大材小用。六、 2026 峰会焦点之三移动端巨头的现实控诉——Android 上的 MGLRU来自荣耀HONOR的王自成在峰会上分享了来自工业界一线、覆盖多达 7000 万台高负载智能手机设备的真实数据。移动端对内存回收有着近乎变态的残酷要求这也彻底暴露了当前标准 MGLRU 的一些阿喀琉斯之踵。1. 毫秒级时间预算与掉帧危机Android 系统的特点是严重超发Overcommits内存极度依赖激进的内存回收来维持流畅度。然而在 120 帧/秒的现代屏幕上回收操作必须死死卡在8.3 毫秒的时间预算内。任何超过该时间的阻塞都会导致 UI 渲染掉帧、卡顿。2. 标准 MGLRU 在 Android 上的四大罪状与荣耀的“魔改”误杀前台文件页相机启动变慢Android 倾向于在应用启动早期预加载大量页面然后靠激进回收清空垃圾。但 MGLRU 经常回收了过多的文件页导致前台应用如相机在需要时必须重新发生缺页读入拉高启动延迟。荣耀的解法引入主动老化Active Aging机制。当应用切到后台时定向加速其页面老化从而将匿名页合理分布到各个世代中同时加入挂钩在回收期间直接跳过前台应用。打碎时间预算超额回收MGLRU 在达到目标水位线后依然会不管不顾地继续回收页面这直接打破了 8.3ms 的时间限制。荣耀的解法强行添加了一个特定的挂钩当时间或水位满足时强行命令 MGLRU 退出。kswapd 内核线程无用功引发线程停顿在直接回收Direct Reclaim中进程经常被强制节流进入睡眠等待kswapd释放内存。然而kswapd却把大量时间浪费在扫描那些根本榨不出油水的控制组cgroup上。现状荣耀目前对此也没有真正的解决方案。预读机制反噬内核预读Read-ahead代码带入的页面会被自动激活这经常激活了大量永远不会被使用的垃圾页面反而排挤了应用程序真正需要的核心页面。3. 来自业界的呼吁从“魔改”走向“通用接口”王自成在会议最后发出呼吁荣耀不希望一直使用这些供应商特定的魔改vendor-specific hacks。内核社区应当为 MGLRU 引入更好的通用接口将控制老化的参数暴露出来。将目前锁在debugfs中的控制节点移到生产系统可用的sysfs中。最重要的一点让 MGLRU 必须具备感知系统运行任务优先级Task Priority的能力不能对前台高优任务和后台冻结任务一视同仁。由于时间耗尽该专题小节在全场若有所思的沉默中结束。七、 结语从传统 LRU 的“非黑即白”到 MGLRU 的“多代同堂”Linux 内存管理的演进是现代硬件需求与极端业务场景如移动端严重超发倒逼软件架构创新的典型范例。2026 年的这场峰会向我们清晰地展示了新技术的落地从来不是一蹴而就的。MGLRU 虽然在多核和大内存上表现优异但它带来了代码冗余、策略冲突以及在特定高响应工作负载如 Android 8.3ms 预算下的不适应。随着宋楷瑞等核心开发者对页面缓存保护、标志位优化的推进以及整个内核社区对mm/vmscan.c大刀阔斧的解耦重构未来的 Linux 内存回收机制终将走向大一统——变成一个既具备多代精准识别能力又兼顾通用性与工业界微调灵活性的单一高效内核子系统。