背景
MGLRU 的回收路径有这道 root 直接回收的检查:当判定为非 root 回收(即 cgroup 级回收)时,即便回收量已达标,扫描也不立即退出。这个检查原本是为了 cgroup 间的回收公平性:在增量 cgroup 迭代恢复之前,公平性依赖遍历目标 memcg 下的每个 memcg 做比例回收,所以即便某个 memcg 提前达标,也不能跳过其余。
┌──────────────────────────────┐
│ 达到回收目标后仍不退出 │
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ 旧检查为组间公平性,现已过时 │ 过度回收随层级规模放大
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ 回收量远超请求 │ 请求 32 页回收 394 页,约 12 倍
└──────────────────────────────┘
后来增量 cgroup 迭代得以恢复,公平性不再依赖遍历每个 memcg。但这道 root 直接回收检查没跟着删,结果 memory.reclaim 达到目标后还在继续扫,回收量远超请求量,而且过度回收的倍数随 cgroup 层级规模放大。
问题
- root 直接回收检查为公平性而设,增量迭代恢复后已过时
- 回收达标后仍不退出,过度回收
- 过度回收倍数随层级规模放大
- Android memory.reclaim 期望回收量与请求量不严重偏离
方案
移除 should_abort_scan 里这道过时的 root 直接回收检查,让回收循环在回收量达标时即退出。
旧:达标不退出
┌────────────────────┐
│ 回收量达标后继续扫 │
└─────────┬──────────┘
▼
┌────────────────────┐
│ 过度回收随层级放大 │
└────────────────────┘
新:达标即退出
┌──────────────────────┐
│ 移除过时的公平性检查 │
└──────────┬───────────┘
▼
┌──────────────────────┐
│ 回收量达标即退出 │ 公平性已由增量迭代保证
└──────────────────────┘
公平性已经由恢复后的增量 cgroup 迭代保证,不再需要靠"强行遍历每个 memcg"来维持。移除后,回收量达标就退出,不再做无谓的额外扫描。
收益
作者提供了量化危害(Android 1-N cgroup 层级,proactive reclaim 实测):
| 指标 | 修复前 | 修复后(预期) |
|---|---|---|
| nr_to_reclaim(请求) | 32 | 32 |
| nr_reclaimed(实际) | 394 | ≈32(达标即退出) |
| 过度回收倍数 | 约 12 倍 | ≈1 倍 |
从机制推断的预期收益:
- memory.reclaim 的实际回收量不再远超请求量,Android 用户态内存管理更精确
- 过度回收浪费的回收 CPU/IO 减少
- 无谓回收导致的后续 refault 减少
作者说明 Android 系统通常用 memory.reclaim 做用户态内存管理,期望回收量与请求量不严重偏离。