内存管理优化 · 方案详解

二十个优化方向,每个方向配齐六件套:现状流程图 → 逐行源码证据 → 优化空间量化 → 方案流程图 → 选型对比 → 风险与落地。源码取自 XNU 与 Linux 官方存档(卡头标注版本与行号,可机械复核);讲解先说人话、再上术语。

XNU:xnu-7195.141.2(iOS 14.8,六版本演化存档 iOS 12→18;IOKit 图形链 deep-read 存档)
Linux:5.10 / 6.1 / 6.6 / 6.12(Android 16)/ 6.18(Android 17)/ master(7.3-rc3)
Android 用户态:AOSP lmkd(Android 14 存档)· 全部结论附行号锚点 · 2026-09

目标 B · 冷热页管理有效性

把冷的找对、把热的护住、把冷的放对地方。四个子问题:判定(谁冷谁热,C1/C3/C6/C12)、保护(热集不误伤,C2)、处置(声明 / 分级放置 / 召回,C4/C5/C7/C10)、覆盖面(决策证据 / 时机 / slab / 容量 / 共享页,C8/C9/C11/C13/C14)。
为什么重要:判定错,压缩/换出/重压全链路白费;热集误伤一次就是用户可感知的卡顿。

目标 A · 内存管理性能

内存管理路径自身跑多快、多省:吞吐(大 folio 批量压缩/换出,P1)、CPU 成本(按页择优压缩 + 卸载出前台路径,P2/P6)、延迟(冷页召回,P3)、编排与鲁棒(异步批量接口 / 压力期定向解救,P4/P5)。
为什么重要:吞吐是冷处置的预算上限——路径越快,压力期越从容,越不需要 kill 止血;CPU 是隐性性能税,按百万页放大。

选向标准(以及为什么有些方向没入选)

直接服务两个目标——记账口径(zram 池归属)、kill 归因类议题服务的是「限额与问责」,不服务性能与冷热有效性,明确不入选;② XNU 已证明价值——Android 17 正沿同一路径收敛(mmd + memory-limiter),方向被产业验证;③ 机制骨架已备——多数方向可从用户态起步,不必等上游。

反向视角:Linux 在 PSI / MGLRU / DAMON / 可配置性上已经领先 XNU(C6 即 Linux 领先项的盘活)。总原则:学 XNU 的语义,用 Linux 的机制表达——不做整体搬入式移植,不推翻 Linux 十年建立的机制 / 策略分层。

二十个方向索引

优先级 ★ = 立即试点价值 / 成本比(★★★ = 最推荐先行;基于厂商视角的收益、成本、风险综合判断)。

术语速查(按出现顺序)

冷页 / 热页

按访问频率区分:长时间没人访问的是冷页,反复被访问的是热页。冷热管理的全部意义:冷的腾地方,热的别动。

MGLRU 世代 (generation)

把页按「最近一次被访问的时间段」分成若干代:最新访问的在 max_gen(最热),最久没碰的在 min_gen(最冷)。MGLRU = Multi-Gen LRU,Linux 6.1 起默认的下一代 LRU。

min_ttl_ms

「最近 N 毫秒内访问过的页不许逐出」的保护窗口,写在 /sys/kernel/mm/lru_gen/min_ttl_ms——目前全局一个值。

refault(回弹)

被逐出的页很快又被访问(缺页读回)。refault 率高 = 逐错了 = 用户感到卡顿。refault 距离 (R−E):内核算出的「这个页值不值得留」的判定数。

zram

用一块 RAM 当压缩池做 swap 的块设备。冷页压缩后仍留在 RAM:召回快,但省得有限。

writeback(回写)

把 zram 里的冷页写到真实存储(NAND)——省 RAM 到极致,代价是召回变成毫秒级 IO,且消耗闪存寿命(有日预算)。

recompression(重压)

对 zram 里已压缩的页用更狠的算法(如 zstd)再压一遍——省 RAM,代价是 CPU。

c_segment(段)

XNU 压缩器的基本打包单位:一段装 N 页的变长槽。元数据 / 搬移 / 换出 / 年龄统计都以段为单位——对照 zram 的逐页模型。

purgeable / volatile

应用主动声明「这块内存可丢弃,需要时我再生成」的机制(iOS 全生态使用)。XNU 用 token 队列精确记账可丢页数。

工作集 (working set)

保持流畅运行必须驻留内存的页集合。低于工作集 = 卡顿;保护工作集 = 保护流畅。

PSI

Pressure Stall Information——「等内存等了多久」的精确度量。它回答「有多堵」,不回答「该不该逐」。

DAMON / DAMOS

Linux 的数据访问监控框架:按「区域」自适应采样(而非逐页),DAMOS 是基于它的操作方案。Pixel 8 GKI 已启用。

THP / mTHP

透明大页:多个 4K 连续页拼成一个大页(映射为一),省 TLB、省 IO 次数。mTHP 是多尺寸版(64K/128K…)。代价:换出 / 压缩按整页算——见 C12。

slab / shrinker

内核小对象(inode/dentry…)的池子叫 slab;每个池子注册 shrinker 回调(count 报库存 / scan 兑现回收)。语义各家一套——见 C11。

jetsam band

XNU 的进程优先级带(0~210):IDLE→BACKGROUND→FREEZER(75)→FG(100)→CRITICAL(190)。内存不够时从低 band 往高 band 杀进程。

LMKD

Android 的用户态内存治理进程(low memory killer daemon):按 PSI / thrashing / oom_adj 排序杀进程。输入全是压力信号——见 C8。

dma-buf heap

Android 图形 buffer 的内核分配接口(system / CMA heap)。6.12 的 system heap 是 buddy 裸分配器,无冷处理语义——见 C10。

流程图图例

起点 / 终点
处理步骤
判定 / 条件
🔴 痛点(现状问题)
✚ 新增 / 改造(方案)
存储 / IO 出口
╌▶反馈环(红虚线)
灰色斜体 = 说明

落地路线图 · 依赖关系 · 度量基线

三阶段 × 谁挡着谁 × 怎么证明有效
顺序原则:先用户态(零内核改动,先攒观测数据)→ 再产品内核补丁(判定 / 保护 / 放置)→ 最后框架级上游(语义 / API)。每个阶段都产出可度量的收益,不是纯研究。

三阶段

短期 0~6 月 · 用户态 / 配置(零内核改动)
  • 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 已开启(很多设备根本没开)。
  • C9 用户态起步:息屏广播驱动的处置窗口(memory.reclaim + mark_idle / writeback 批处理,亮屏即中止)——与 C4(a) 同批零内核改动;C14 观测:memory.stat 补 shared 统计先跑起来。
依赖:Android 14 GKI(6.1)起 · 收益:存量设备获得 Android 17 的主要内存收益;反馈数据开始积累
中期 6~18 月 · 产品内核补丁
  • 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 上游化。
  • C8 处置证据:per-memcg 处置状态导出(tracepoint)→ LMKD kill 排序输入 + thaw 豁免窗口。
  • C11 / C14:list_lru per-memcg 记账完善 + shared 计数接口(批量 + 缓存)。
  • P6 压缩卸载:压缩请求入队 + worker 池(小核亲和),紧急同步兜底。
  • C13 zram 弹性:slot 元数据分段懒分配 + 扩容路径(vendor 分支试点)。
依赖:GKI vendor hook 或上游化 · 收益:冷判定质量、热集保护、放置正确性、召回延迟
长期 · 框架级上游
  • C5 volatile API:memfd ioctl + 图形栈(gralloc / dmabuf heap)接入——SurfaceFlinger buffer cache 是最大受益者。
  • P1 zram 大 folio 压缩:等 RFC 重启,或厂商先行试点再反哺上游。
  • C6 DAMOS 编排产业化:ART heap / 浏览器的区域级处置。
  • C10 图形 purgeable:dma-buf heap volatile 语义 + 记账即时切换(与 C5 同一套 ioctl)。
  • P4 异步接口:memory.reclaim_async(提交即返回 + 完成通知 + 批量 memcg)。
  • P5 定向解救:vmscan 三态流控(检测 → 解救 → 观察),参数反馈化。
  • C12 温度驱动 THP:khugepaged / deferred split 接世代年龄(vendor hook 试点)。
依赖:主线 mm 社区共识 · 收益:语义级对齐 XNU,生态级收益

依赖关系(谁挡着谁)

方向前置依赖下游受益
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 的激进处置空间
C8 kill 证据C4 / C1 / C3(处置证据与温度)无下游——冷处置投资的「回报出口」
C9 窗口调度(用户态起步)C4(窗口编排)、P3(resume 预热联动)
C10 图形冷处置C5(同一套 volatile 语义)图形栈 buffer cache(最大可再生品类)
C11 slab 回收C3(压力档位接口)C8(zone 耗尽 kill 语义化)
C12 THP 温度C1(温度输入)C7(拆出页放置)、P1(整页热时批量)
C13 zram 弹性C1 / C7(供给预测 + 腾空放置)P4(异步伸缩操作)
C14 共享页去重C4 / C1(冻结管线 + 世代扫描)C8(低私有占比 kill 排序)
P4 异步接口C4 / C9 / C13——用户态编排基础设施
P5 流控解救C3(无进展原因观测)C8(解救阶梯)、C2(偏置不击穿 floor)
P6 压缩卸载P2(worker 上跑择优)P1(批量 folio 使 worker 更饱满)

读法:C3 和 C1 是「公共底座」——先做观测(C3 前半)再上温度(C1),是全局最优路径;C5 / C6 / P1 / C9 / P4 无前置,随时可并行启动。

怎么证明有效(度量基线)

方向首选度量验收基线(示例)
C1 / C2 / C3per-memcg refault 率(file/anon 分路)、PSI some满载场景 refault 率 ↓ ≥ 20%,PSI some 有可测下降
C4冻结 app 内存驻留(memory.current)、thaw 首帧时间驻留 ↓ ≥ 50%;thaw 首帧 < 800ms
C5实际丢弃量、通知后重生成的 CPU丢弃量 > 0 且无前台卡顿回归
C6DAMON 采样 CPU 占用、区域处置时延采样 ≤ 1% CPU
C7NAND 日写入量 vs 预算、zram 同页重入率日写入 ≤ 预算(mmd 为 24GiB/日级)
P1压缩吞吐(页 / 秒)、per-page CPU吞吐 ≥ 2×
P2首次压缩 CPU 均值、整体压缩比CPU ↓ ≥ 15%,压缩比不降
P3换入 P99 延迟、thaw 总时长P99 ↓ ≥ 30%
C8避免的 kill 数(灰度对照组)、thaw 后豁免命中率每 BLE / 重载会话避免 kill ≥ 1 次
C9息屏窗口内 writeback / 重压占比、前台卡顿回归窗口内重处置 ≥ 80%,前台无回归
C10丢弃量、footprint 阶跃、重生 CPU阶跃对齐 XNU 量级(真机 768MB)
C11slab 回收时延 P99(触发 → 释放)压力早期参与、末期不雪崩
C12折叠-拆分振荡率、回收期拆分量振荡率趋 0,回收期拆分 ↓
C13容量错配时长、元数据占用低负载设备元数据 ↓ ≥ 30%
C14同物理页重复处置率重复处置率 ↓ ≥ 50%
P4提交 → 返回延迟、完成通知到达率提交即返回(< 1ms)
P5无进展周期数、解救成功率死锁自解除时延 ↓ ≥ 50%
P6前台线程压缩时间占比前台压缩 CPU 占比 → ~0

达标线为示例基线(首发试点的验收建议),非上游承诺;每个试点应同时跑对照组(同机型同负载不开特性),用同一套 tracepoint 取数。