内存管理优化 · 方案详解
十个优化方向,每个方向配齐六件套:现状流程图 → 逐行源码证据 → 优化空间量化 → 方案流程图 → 选型对比 → 风险与落地。源码取自 XNU 与 Linux 官方存档(卡头标注版本与行号,可机械复核);讲解先说人话、再上术语。
Linux:5.10 / 6.1 / 6.6 / 6.12(Android 16)/ 6.18(Android 17)/ master(7.3-rc3)
全部结论附行号锚点 · 2026-09
目标 B · 冷热页管理有效性
把冷的找对、把热的护住、把冷的放对地方。三个子问题:判定(谁冷谁热,C1/C3/C6)、保护(热集不误伤,C2)、处置(声明 / 分级放置 / 召回,C4/C5/C7)。
为什么重要:判定错,压缩/换出/重压全链路白费;热集误伤一次就是用户可感知的卡顿。
目标 A · 内存管理性能
内存管理路径自身跑多快、多省:吞吐(大 folio 批量压缩/换出,P1)、CPU 成本(按页择优压缩,P2)、延迟(冷页召回,P3)。
为什么重要:吞吐是冷处置的预算上限——路径越快,压力期越从容,越不需要 kill 止血;CPU 是隐性性能税,按百万页放大。
✦选向标准(以及为什么有些方向没入选)
① 直接服务两个目标——记账口径(zram 池归属)、kill 归因类议题服务的是「限额与问责」,不服务性能与冷热有效性,明确不入选;② XNU 已证明价值——Android 17 正沿同一路径收敛(mmd + memory-limiter),方向被产业验证;③ 机制骨架已备——多数方向可从用户态起步,不必等上游。
反向视角:Linux 在 PSI / MGLRU / DAMON / 可配置性上已经领先 XNU(C6 即 Linux 领先项的盘活)。总原则:学 XNU 的语义,用 Linux 的机制表达——不做整体搬入式移植,不推翻 Linux 十年建立的机制 / 策略分层。
✦十个方向索引
优先级 ★ = 立即试点价值 / 成本比(★★★ = 最推荐先行;基于厂商视角的收益、成本、风险综合判断)。
✦术语速查(按出现顺序)
按访问频率区分:长时间没人访问的是冷页,反复被访问的是热页。冷热管理的全部意义:冷的腾地方,热的别动。
把页按「最近一次被访问的时间段」分成若干代:最新访问的在 max_gen(最热),最久没碰的在 min_gen(最冷)。MGLRU = Multi-Gen LRU,Linux 6.1 起默认的下一代 LRU。
「最近 N 毫秒内访问过的页不许逐出」的保护窗口,写在 /sys/kernel/mm/lru_gen/min_ttl_ms——目前全局一个值。
被逐出的页很快又被访问(缺页读回)。refault 率高 = 逐错了 = 用户感到卡顿。refault 距离 (R−E):内核算出的「这个页值不值得留」的判定数。
用一块 RAM 当压缩池做 swap 的块设备。冷页压缩后仍留在 RAM:召回快,但省得有限。
把 zram 里的冷页写到真实存储(NAND)——省 RAM 到极致,代价是召回变成毫秒级 IO,且消耗闪存寿命(有日预算)。
对 zram 里已压缩的页用更狠的算法(如 zstd)再压一遍——省 RAM,代价是 CPU。
XNU 压缩器的基本打包单位:一段装 N 页的变长槽。元数据 / 搬移 / 换出 / 年龄统计都以段为单位——对照 zram 的逐页模型。
应用主动声明「这块内存可丢弃,需要时我再生成」的机制(iOS 全生态使用)。XNU 用 token 队列精确记账可丢页数。
保持流畅运行必须驻留内存的页集合。低于工作集 = 卡顿;保护工作集 = 保护流畅。
Pressure Stall Information——「等内存等了多久」的精确度量。它回答「有多堵」,不回答「该不该逐」。
Linux 的数据访问监控框架:按「区域」自适应采样(而非逐页),DAMOS 是基于它的操作方案。Pixel 8 GKI 已启用。
✦流程图图例
落地路线图 · 依赖关系 · 度量基线
①三阶段
- C4(a) 存量回移:Android 14~16 复刻 mmd 管线——cgroup v2 freeze + memory.reclaim(6.1+ 可用;swappiness= 选项需 6.12+)+ 全局 mark_idle / writeback。
- C3 前半:回读率 / refault 观测——tracepoint + debugfs 直方图,先攒 1~2 个版本周期的数据,再谈稳定接口。
- MGLRU 调优:min_ttl_ms 真正用起来;确认 same-filled / recompression 已开启(很多设备根本没开)。
- C1 世代年龄接入:TRACK_ENTRY_ACTIME + MGLRU 世代时间戳 → slot 温度(C2-C7 / P2 / P3 的公共输入)。
- C2 工作集 floor:min_ttl_ms per-memcg 化 + refault 反馈调参。
- C7 放置策略层:三输入决策(压缩收益 × 召回概率 × 闪存成本)。
- C3 后半:PSI 风格稳定接口 · P3:zram 大 folio 换入 · C4(c):per-process writeback ioctl 上游化。
- C5 volatile API:memfd ioctl + 图形栈(gralloc / dmabuf heap)接入——SurfaceFlinger buffer cache 是最大受益者。
- P1 zram 大 folio 压缩:等 RFC 重启,或厂商先行试点再反哺上游。
- C6 DAMOS 编排产业化:ART heap / 浏览器的区域级处置。
②依赖关系(谁挡着谁)
| 方向 | 前置依赖 | 下游受益 |
|---|---|---|
| C1 温度 | C3(观测数据先攒 1~2 个版本周期) | C4 / C7 / P2 / P3——温度是它们的判定输入 |
| C2 保护 | C3(refault 反馈调参) | 全部冷处置方向的「安全网」 |
| C3 反馈 | 无(tracepoint 观测可立即做) | C1 / C2 的自动调参 |
| C4 冻结处置 | C1(替代 2h~4h 空闲时钟) | P3(thaw 召回风暴对冲) |
| C5 声明式冷页 | 无 | 图形栈(buffer cache 可再生页) |
| C6 DAMON | 无(Pixel 8 GKI 配置已启用) | ART heap、浏览器大地址空间 |
| C7 放置 | C1(召回概率输入) | 闪存寿命预算管理 |
| P1 批量压缩 | 无(可等上游 RFC 或厂商先行) | C4 / C7 的 writeback 吞吐 |
| P2 择优压缩 | C1(只重压「将长期冷」的页才划算) | 前台 CPU 余量 |
| P3 召回延迟 | C1(召回窗口预测) | C4 / C7 的激进处置空间 |
读法:C3 和 C1 是「公共底座」——先做观测(C3 前半)再上温度(C1),是全局最优路径;C5 / C6 / P1 无前置,随时可并行启动。
③怎么证明有效(度量基线)
| 方向 | 首选度量 | 验收基线(示例) |
|---|---|---|
| C1 / C2 / C3 | per-memcg refault 率(file/anon 分路)、PSI some | 满载场景 refault 率 ↓ ≥ 20%,PSI some 有可测下降 |
| C4 | 冻结 app 内存驻留(memory.current)、thaw 首帧时间 | 驻留 ↓ ≥ 50%;thaw 首帧 < 800ms |
| C5 | 实际丢弃量、通知后重生成的 CPU | 丢弃量 > 0 且无前台卡顿回归 |
| C6 | DAMON 采样 CPU 占用、区域处置时延 | 采样 ≤ 1% CPU |
| C7 | NAND 日写入量 vs 预算、zram 同页重入率 | 日写入 ≤ 预算(mmd 为 24GiB/日级) |
| P1 | 压缩吞吐(页 / 秒)、per-page CPU | 吞吐 ≥ 2× |
| P2 | 首次压缩 CPU 均值、整体压缩比 | CPU ↓ ≥ 15%,压缩比不降 |
| P3 | 换入 P99 延迟、thaw 总时长 | P99 ↓ ≥ 30% |
达标线为示例基线(首发试点的验收建议),非上游承诺;每个试点应同时跑对照组(同机型同负载不开特性),用同一套 tracepoint 取数。