# XNU × Android/Linux 内存管理机制对比与 Linux 优化方向

> 基于一手源码的深度分析：XNU 侧以 xnu-7195.141.2（iOS 14.8）为主锚点、六版本演化存档（iOS 12→18）为纵向参照；
> Linux 侧覆盖 5.10（传统 LRU）→ 6.1（MGLRU）→ 6.6 → **6.12（Android 16 GKI 基线）** → **6.18（Android 17 GKI）** → **7.3-rc3（主线最新）**；
> Android 侧含 lmkd 源码存档、Pixel 8 实机 GKI 配置实证与 Android 17 官方文档核验（mmd / memory-limiter）。
> 全部行号引用指向本地存档文件（附录 A 给出文件对照表），可机械复核；配套调研底稿 `linux-mm-android-research-notes.md` 含全部来源 URL。

---

## 0. TL;DR

**三个根本分野：**

1. **策略位置**：XNU 把进程查杀做成内核一等公民（memorystatus 单线程健康检查循环，210 个优先级 band）；Linux/Android 把杀进程放在用户态（lmkd + PSI + oom_score_adj），内核只提供机制。前者低延迟、信号全、可归因；后者可 OTA、可试验、生态开放。
2. **压缩器形态**：XNU 是"每任务打包式"压缩器（c_segment 变长槽 + 压缩页计入任务 phys_footprint + 按年龄整段换出 NAND）；Linux 是"逐页独立"压缩（zram/zsmalloc）——swap 记账有（memcg swap 口径）、压缩池物理页记账无（root 黑洞）、上游无年龄驱动的自动换出（Android 17 的 mmd 正在补齐，见 §3.8）。
3. **挂起语义**：XNU 的 freezer 是"定向换出"（挂起 app → 全量压缩 → 写 NAND → 提升到保护 band）；Linux 的 cgroup freezer 只停 CPU 不搬内存——但 **Android 17（mmd + memory-limiter）已开始把"冻结×回收×换出×保护"管线在用户态重新拼装出来**，本分析提出的两条核心方向（C1/C4）由此从"提案"变为"补齐差距"（§3.8）。

**十个优化方向**（详见 §4，围绕两个目标）：**冷热页管理有效性 C1-C7**——世代年龄老化、工作集保护、抖动反馈环、冻结进程冷页处置、应用声明冷内存、DAMON 区域冷热识别、冷页分级存储；**内存管理性能 P1-P3**——大 folio 批量压缩换出、按页择优压缩、冷页召回延迟。

**一个反向结论**：Linux 在压力定量测量（PSI）、页表遍历式老化（MGLRU + Bloom filter 反馈）、可配置性/可追溯性上已领先 XNU，优化方向不是"照抄 XNU"，而是把 XNU 证明有价值的**语义**嫁接到 Linux 已有的**机制骨架**上。

---

## 1. 版本基线与证据口径

### 1.1 XNU 侧

| 项 | 值 |
|---|---|
| 主锚点版本 | xnu-7195.141.2（iOS 14.8） |
| 演化对照 | iOS 12 (4903) / 13 (6153) / 14 (7195) / 16 (8792) / 17 (10063) / 18 (11215) |
| 设计文档 | xnu 自带 `doc/vm/*.md`（memorystatus / kills / freezer / pageout_scan / notify，Apple 内部文档随源码开源，本仓已存档） |
| 实测数据 | iPhone 12 Pro / iOS 14.8 / 16K 页真机实验（压缩器吞吐、SV 页、老化实验等，锚点笔记第五/十批） |

### 1.2 Linux / Android 侧

| 版本 | 地位 | 本地存档 |
|---|---|---|
| 5.10 | 传统 LRU 末期（Android 12/13 GKI） | `linux-5.10_vmscan510.c`、`linux-5.10_rmap510.c` |
| 6.1 | MGLRU + `memory.reclaim` 合入（Android 14 GKI） | `linux-6.1_mm_vmscan.c` 等 11 文件 |
| 6.6 | Android 15 GKI | `v6.6/mm/*`（test1 主书存档） |
| **6.12** | **Android 16 GKI 基线**（android16-6.12-2025-12 发布分支） | `linux-6.12_*`（本次新抓） |
| **6.18** | **Android 17 GKI**（android17-6.18；2025-11 发布） | 核心文件未存档（官方源不可达），事实经官方文档核验 |
| **7.3-rc3** | 主线最新（kernel.org releases.json 2026-09-13；2026 年中版本号进入 7.x） | `linux-master_*`（本次新抓） |

关键事实的逐版本核验记录（本会话完成，方法见附录 B；调研底稿见配套文件 `linux-mm-android-research-notes.md`）：
- zswap objcg 记账：v6.8 中 `obj_cgroup` 0 处引用 → **v6.9 起 10 处**（压缩页记账归还原始 cgroup 自 6.9 落地）；
- `is_exec_file_folio`（可执行文件页回收保护）：v7.0 / v7.1 / v7.2 均 0 处 → **仅存在于 7.3-rc3**（最新特性）；
- MGLRU `min_ttl_ms`：**6.1 已存在**（本仓 6.1 存档 L5381 `__ATTR(min_ttl_ms, …)`；调研笔记中的"v6.3 首现"经查为文档提交 7b8144e63d84（+15 行 rst），非功能引入，已修正）；
- `memory.reclaim`：**v6.1 起存在**（v6.1 memcontrol.c:6583 函数体核验；swappiness 选项 6.12 存档确证在位，更早合入版本未核验）；
- zram：recompression 合入 **v6.2**（84b33bf78889，GitHub compare 核验）；chained recompression 已于 ~7.0 **移除**（cedfa028b54e，2026-04）；6.12 有 writeback_limit；7.3-rc3 重构出 pp_slot 后处理线程与批量 writeback；**大 folio 支持截至 2026-08 未合入**（torvalds 树 2022-04~2026-08 全部 200 个 zram 提交核对无 large folio；Barry Song RFC 系列未落地）；zswap 大 folio swap-out 6.13 合入（kernelnewbies 6.13 条目）；
- **Android 17（GKI 6.18）已量产 XNU 式管线**（官方文档核验，详见 §3.8）：mmd 守护进程（zram recompression/writeback 日常维护，idle 阈值 2-4h 动态、日预算 24GiB）+ memory-limiter 系统服务（memory.high + memory.swap.max 按可见性分档限额）+ per-process zram writeback/prefetch ioctl；
- Android 16K page：Play 要求 targeting API 35+ 的 64 位应用必须支持 16K，**2027-02-01 强制**；Pixel 8/9 系列已支持 16K 启动（开发者选项）；官方数据：内存压力下应用启动时间平均 **-3.16%**；
- swap 大 folio：v6.14 memory.c 已有 `can_swapin_thp`（L4147）与 `swap_zeromap_batch`，注释明言"zswap 暂不支持大 folio"（该限制 6.13 起已解除，见上）；swap-in large folio（同步 IO 设备）提交 242d12c98174（2024-09，约 6.12 窗口）；
- **Android GKI 实机配置实证**（Pixel 8 / gs201 / Android 14 时代 GKI 6.1，lineage-21 分支 gki_defconfig，已存档）：`CONFIG_LRU_GEN=y`（MGLRU 默认启用）、`CONFIG_DAMON=y + DAMON_PADDR=y + DAMON_RECLAIM=y`（DAMON 主动回收机制在位）、`CONFIG_ZRAM=m + CONFIG_ZRAM_WRITEBACK=y`、`CONFIG_PSI=y`、`CONFIG_MEMCG=y`、`CONFIG_CGROUP_FREEZER=y`、`CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y`——本节涉及的机制在真实 Android 设备上全部在位。

> ⚠️ 网络受限说明：本会话 web_search 不可用，Android 官方 googlesource 域不可达。所有"已核验"标记均来自 raw.githubusercontent.com 单文件逐版本比对；标【待核验】处依赖模型知识/公开记录，置信度单独说明。

### 1.3 引用格式

`[XNU:文件:行]` 指 xnu-7195.141.2 存档；`[XNU18:…]` 指 xnu-11215.1.10；`[L5.10/L6.1/L6.12/L7.3:文件:行]` 指对应 Linux 版本存档；`[LMKD:行]` 指本地 lmkd.cpp 存档；`[A17:页面]` 指 source.android.google.cn 官方文档（lmkd / mmd / memory-limiter / cached-apps-freezer / 16kb-page-size，调研底稿含 URL）；`[EXP]` 指真机实测数据。

---

## 2. 设计哲学分野：一体化策略内核 vs 分层机制框架

| 维度 | XNU（iOS） | Linux/Android |
|---|---|---|
| 回收决策主体 | 内核 `vm_pageout_scan` 单循环 + `memorystatus_thread` 健康检查 | 内核 kswapd/direct reclaim（机制）+ lmkd/oomd（策略） |
| 查杀 | 内核内，210 band，15 类 kill reason | 用户态 lmkd（Android）/ systemd-oomd（服务器）；内核 OOM 为兜底 |
| 内存压力信号 | 5 级枚举（Normal/Warning/Urgent/Critical/Jetsam）+ kevent/dispatch | PSI（some/full，时间定量，cgroup 级）+ vmpressure + watermarks |
| 压缩 | 每任务打包段（c_segment），可整体搬移 | 全局块设备（zram）或前端池（zswap），逐页独立 |
| 挂起 | freezer = 定向换出 + 保护 band | cgroup freezer = 仅停 CPU |
| 可丢弃内存 | purgeable token（有序丢弃、记账切换） | MADV_FREE（无序惰性丢弃） |
| 记账 | phys_footprint 单一口径（含压缩页+页表+IOKit） | RSS / anon / swap / memcg 多口径并存 |

一句话概括：**XNU 假设"整机只有一个调度者"（RunningBoard + memorystatus 联动，生命周期感知），把杀进程当作正常回收路径；Linux 假设"谁都可以来跑"，把杀进程当作策略域，内核只出卖机制。** 两者的差异不是优劣，是约束不同——但 XNU 在"移动端整机语义"上趟出来的机制，相当一部分在 Linux 上还没有等价物，这就是优化空间。

---

## 3. 六大机制逐项对比

### 3.1 页面回收：五阶段扫描 vs MGLRU 世代

**XNU `vm_pageout_scan`**（设计文档 pageout_scan.md + [XNU:vm_pageout.c]）：

- 五阶段：初始化+快速回收（pmap_release_pages_fast）→ 页队列管理 → 流控判定 → 受害者选择 → 回收执行；
- **2:1 匿名:文件偏向**：来自"回读文件页代价≈匿名页两倍"的磁盘时代经验，四种极端条件可覆盖（文件缓存低于下限 / 非活跃文件页<50% / 空闲页危险地低→反而偏向文件）；
- **FCS 流控状态机**（FCS_IDLE/DELAYED/DEADLOCK_DETECTED）：压缩队列满载时切换回收偏向、唤醒压缩器 GC、提高回收目标——与 Linux `too_many_isolated` / `reclaim_throttle`（[L6.12:vmscan.c:520]）同构；
- **回收不前进 → 直接杀**：连续被迫重激活的页强制回收，累计一定数量后**pageout 线程内直接调 `memorystatus_kill_on_VM_page_shortage`**（[XNU:vm_pageout.c:2730]，CONFIG_JETSAM 分支）——"LRU 近似失效时，回收让位于查杀"；
- 水位阶梯：`available_pages_pressure / critical` + 15% 迟滞（[XNU:vm_pageout.c:10935-10987]）；
- 历史实验（Apple 设计文档原文）：基于缺页延迟测量的 latency-jetsam 因 LPF 特性延迟太大被废弃；pageout 线程动态优先级传播导致音频毛刺被禁用——**两个失败的智能化尝试**，说明该领域简单机制+好阈值 > 复杂自适应。

**Linux 演化**：

- 5.10 及以前：active/inactive 双链 + 反向映射引用检测（[L5.10:rmap.c:850 page_referenced]），全体扫描 O(n)，手机端 kswapd CPU 占用与卡顿问题突出；
- 6.1 MGLRU：世代化（max_seq/min_seq），`lru_gen_look_around` 空间局部性批量扫描（[L6.1:vmscan.c:4589]）；
- 6.12：**rmap/PT-walk 反馈环**——扫描中发现热页时顺带扫相邻 PTE，PMD 级 Bloom filter 记录"值得再访"的页表区域（[L6.12:vmscan.c:4037-4042]），把老化成本从"全页表遍历"降为"按线索访问"；**`min_ttl_ms` 工作集保护**——保护最近 N 毫秒工作集不被驱逐，保不住直接触发 OOM（[L6.12:vmscan.c:3995, 5118-5137]；admin 文档明确"基于人眼可感卡顿 ~100ms，N=1000"）；
- 7.3-rc3：**可执行文件页保护** `is_exec_file_folio`（[L7.3:vmscan.c:267]，仅 7.3 存在）——exec 页被逐出后的回读 IO 是移动端卡顿大项；kswapd `hopeless` 检测（回收失败累计达到 MAX_RECLAIM_RETRIES 后停止无效空转，仅在节点重新平衡时清除，[L7.3:vmscan.c:7631-7642]）。

**对比洞见**：

1. **MGLRU min_ttl + OOM 兜底 ≈ XNU "保不住工作集就杀"的思路趋同**——Linux 首次在内核里把"驱逐换不来健康"升级为"杀进程"（min_ttl 保不住 → out_of_memory，[L6.12:vmscan.c:4017-4030]）。这是两套体系最重要的收敛点。
2. XNU 的"回收不前进→杀"（pageout 内联触发）在 Linux 上除 min_ttl 外仍无对应：direct reclaim 停在 throttle/NOPROGRESS，最终等 OOM 或 lmkd 的 PSI 事件——**延迟更长**。
3. XNU 2:1 偏向的"可覆盖条件"设计 vs Linux swappiness 单参数（6.12 起 memory.reclaim 支持按次覆盖 swappiness，7.3 增 `swappiness_max=anon_only`，[L7.3:vmscan.c:7991]）——Linux 正在把"文件/匿名平衡"从全局静态参数变为每次回收的显式参数，方向正确。

### 3.2 内存压缩：打包段 vs 逐页

**XNU 压缩器**（[XNU:vm_compressor.c] + 真机实测）：

- **c_segment 打包**：每段含一批压缩页，变长槽（1KB 对齐），段是压缩、老化、换出、搬移的基本单位——这使"整段换出到 NAND"成为可能（freezer 依赖）；
- **算法混合**：metacompressor 首次压缩即按页择优（WKdm 默认，`sz >= lz4_threshold(16K页=12288)` 才试 LZ4，LZ4 失败有负反馈学习 compressor_selector_update，[XNU:vm_compressor_algorithms.c:140-212]）。⚠️ 真机实证：本机 RELEASE 构建中 tuneables 使自适应休眠，实际 WKdm 单一生效（锚点笔记第十批，perpage=14289 精确等于 WKdm 输出）——开源默认 ≠ 设备行为，教训是**压缩策略的可配置性与默认值同样重要**；
- **特殊页处理**：单值页（全页同一 32bit 值）走 sv_hash，解压≈memset（实测 MOSTLY_ZEROS 解压 52GiB/s，[XNU:vm_compressor.c:3952-3960]）；不可压页原样 memcpy 存储（c_segment_noncompressible_pages++，L3935-3946）；
- **年龄驱动换出**：`compressor_needs_to_swap`（[XNU:vm_compressor.c:2160-2237]）——段年龄 ≥ vm_ripe_target_age 判定"熟透"可换出；`compute_swapout_target_age` 用解压回读率反推抖动，检测到抖动时主动换出+触发 `memorystatus_kill_on_VM_compressor_thrashing`（L2248）；phantom cache 检测"同一数据换出又读回"驱动 FCTHRASHING kill；
- **性能**（真机 16K 页）：WKdm 压缩 1595 / 解压 1980 MiB/s（合成）；真实 app 脏页 774-793 / 991-1104 MiB/s、压缩比 0.40-0.67；突发摄入峰值 ~580MB/s；空闲>1.3GB 时完全空闲（事件驱动）。

**Linux zram/zswap**：

- zram：逐页压缩进 zsmalloc size class；**同值页去重** `page_same_filled`（[L6.12:zram_drv.c:205,1507]，与 XNU sv_hash 同构）；**recompression 二级算法**（v6.2 合入）——先低成本算法压缩，后台按阈值把"仍偏大"的页用 zstd 重压（[L6.12:zram_drv.c:1658 zram_recompress / 1804 recompress_store]）；**writeback**：idle/huge/huge_idle/incompressible 四模式换出到存储 + `writeback_limit` 预算（[L6.12:zram_drv.c:588-647]）——但**单页同步 IO**（L697-700 原文注释"XXX: A single page IO would be inefficient…as starter"）；
- 7.3-rc3 重构：`zram_pp_slot` 后处理槽 + 批量 writeback（`zram_writeback_slots`/`scan_slots_for_writeback`）+ 新 `compressed_writeback` 设备属性 + `mark_slot_accessed` 世代标记——zram 正在向"后台批量后处理"演化；
- zswap：v6.9 起压缩页记账到 obj_cgroup（[L6.12:zswap.c:193-202, 801]）；zsmalloc/zram 无此记账。

**差距分析**：

| 维度 | XNU | Linux | 差距实质 |
|---|---|---|---|
| 压缩粒度 | 段打包（变长槽） | 逐页 | Linux 无"可整体搬移的压缩单元" |
| 首次算法选择 | 按页择优（设计上） | 设备级固定算法 + 二次重压 | Linux 的 recompression 是补丁式，非首次择优 |
| 换出驱动 | 段年龄 + 抖动反推 | 上游手动 mark_idle（Android 17 mmd 已自动化：idle 2-4h 空闲时钟） | 上游无自动策略；mmd 缺世代年龄与抖动反馈 |
| 换出预算 | NAND 日预算（存储团队 IOCTL 下发） | writeback_limit + mmd 日预算 24GiB | Android 17 已对齐思路 |
| 记账 | internal_compressed 计入任务 | swap entry 记 memcg；zswap 池记 objcg；zram 池记 root | zram 压缩池物理页无归属（§3.6 第②层） |
| 同值页 | sv_hash | same_filled | 两边对齐 |

### 3.3 挂起进程处理：定向换出 vs 仅停 CPU

**XNU freezer**（设计文档 freezer.md，全文为本仓存档）：

- 触发：内存有一定压力但还没到 jetsam 阈值时，freezer 线程把**挂起**的 app 冻结：`vm_map_freeze` 两遍扫描（先评估 dirty 匿名/共享比，再压缩全部脏页进专用 freezer 段），段进 swapout 队列由专职线程写 NAND，进程提升到 **band 75**（保护 band，jetsam 基本不动它）；
- 换入：thaw 时不预加载，按缺页从 NAND 拉回（"just like macOS/iPad swap"）；再次挂起可 re-freeze，已在 NAND 上的数据不重读不重写（vm_compressor_relocate 跳过 C_SEG_IS_ON_DISK_OR_SOQ）；
- 退出：每日 demotion 至多 2 个进程（4 个在 app-swap iPad），按 thaw 次数挑选，demotion 只是降回 idle band 等 jetsam 收割，脏数据留在 NAND；
- **NAND 写预算**：按日窗口记账，预算值通过 IOCTL 向存储设备查询（`vm_swap_vol_get_budget`），未用完滚存——闪存寿命是一等约束；
- 共享内存约束：冻结进程的共享页设上限（per-process + 全局 + 私有/共享比），本质是防"冻结者与活进程共享导致换出污染"；coalition 整组冻结；Safari 每个 WebContent（tab）独立冻结决策（WebKit 通过 `MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE` SPI 逐 tab 开关）；
- 选择策略：idle band LRU。**Apple 试过预测模型选冻结对象，A/B 实验没跑赢 LRU**（rdar://83112455）——与 pageout 的两个失败实验并列，第三个"简单胜过智能"的实例。

**Linux/Android**：

- cgroup v2 freezer：`__refrigerator` 就是 `schedule()` 循环（[L6.1:kernel/freezer.c:62-90]），**只停 CPU，一个字节都不搬**；Android 11 QPR3+ 起的 cached app freezer 即基于它（cgroup v2 层级 `/sys/fs/cgroup/uid_*/cgroup.freeze`，[A17:cached-apps-freezer]）；
- **Android 11~16 的冻结内存收益为零**（只省 CPU/防后台作恶）：冻结 app 的匿名内存仍全量驻留，只能等全局 LRU 在压力下慢慢驱逐——驱逐顺序由全局年龄决定，与"这个 app 是否已挂起"无关；
- **Android 17 起出现拐点**（[A17:mmd/memory-limiter]，详见 §3.8）：cached → frozen → **尽量回收** → 对"杀掉会被用户感知"的 cached app 做 **per-process zram writeback**（`ZRAM_ANDROID_IOC_PROCESS_WRITEBACK` ioctl）→ **反向提升其 oom_score_adj**（更难被杀）→ 解冻时 `ZRAM_ANDROID_IOC_PROCESS_PREFETCH` 预取缩短启动延迟；
- 7.3-rc3 的交互信号：`user_proactive_reclaim` 在任务被冻结时返回 -ERESTARTSYS 让 syscall 重启（[L7.3:vmscan.c:8004-8012]）——内核层面"冻结态任务做回收"的语义开始被正视。

**差距实质**（以 Android 16 及以下为基准）：Android 冻结 app 后内存收益为零（只省 CPU）；XNU 冻结后内存收益≈全量脏页。8GB 设备 20 个后台 app 各 300MB 驻留 = 6GB 死重，XNU 场景下这 6GB 会变成 NAND 上的压缩数据。**Android 17 正在补这一课**，但实现路径与 XNU 不同：用户态编排（mmd/memory-limiter）+ vendor ioctl，而非内核内建管线——差距与机会见 §3.8 与 C1/C4。

### 3.4 可丢弃内存：purgeable token vs MADV_FREE

**XNU purgeable**（[XNU:vm_purgeable.c] + skill 第八批 IOKit 分析）：

- token 状态机：对象进入 volatile 时在 token 队列（FIFO/LIFO/obsolete 三类 + ordering 修饰位）领 token，页数计入 token；`token_q_new_pagecount` 惰性累积、`available_for_purge` 即时可丢计数（[XNU:vm_purgeable.c:62-88]）；
- 丢弃：压力下 pageout 扫到"ripe"的 purgeable 对象**整对象批发丢弃**（pageout_scan Phase 4"ripe purgeable vm-object"），丢弃顺序由队列类型决定——应用可以声明"先丢我这块"；
- 记账切换：volatile 页不计入 phys_footprint（只有 purgeable_nonvolatile 计入）——应用"可丢缓存"和"必须保留"在账本上天然分开；
- 生态：VM 机制（token）→ IOKit 翻译层（purgeableControlBits 四状态直译表，IOMemoryDescriptor.cpp:245-270）→ IOSurface（像素数据可重渲染，purgeable 最重用户）；WebKit 解码位图、SpringBoard 背景图全是用户。

**Linux**：

- MADV_FREE：清 young+dirty 位、`folio_mark_lazyfree`（[L6.12:madvise.c:642-782]），此后页在回收压力下"若仍干净则直接丢"。语义弱点：**丢弃无序**（LRU 说了算）、**无丢弃通知**（应用不知道缓存没了）、**无记账切换**（lazyfree 页仍算 anon RSS，memcg 仍全额记账直到真丢）、只对匿名页有效；
- 历史：John Stultz 的 volatile range patchset（2013）未合入；Android ashmem 的 pin/unpin 语义是 purgeable 的真正等价物，但 ashmem 正被 memfd/dmabuf 取代，**unpin 语义没有跟着迁移**——这是 Android 侧一个正在发生的语义倒退。

### 3.5 查杀策略：210 band × 15 reason vs PSI × oom_score_adj

**XNU jetsam**（memorystatus.md / kills.md + [XNU:kern_memorystatus.c]）：

- 210 个优先级 band（0=idle / 30=background(dock) / 75=freezer / 100=foreground / 160=SpringBoard / 190=Critical），全局 TAILQ 数组，kill 沿 band 升序"行刑"；band 由 RunningBoard 按生命周期断言动态设置（前台/挂起/dock/aging band 10-5-2 秒……）；
- **15 类 kill reason**（kills.md 全表）：高水位软限额、vnode 耗尽、页短缺、proc thrashing（假 idle）、**FC thrashing（phantom cache）**、per-process 硬限额、磁盘空间、idle exit、zone map 耗尽、**VM compressor thrashing**、compressor 空间、低 swap、pageout 饥饿、**持续压力 10 分钟**、conclave 限额——每类有独立触发条件与行刑策略（是否升 band、杀几个、在哪个线程上下文）；
- per-process 双限额：软限额超限→高水位 kill（有压力才杀）+ 80% 预警 kevent；硬限额→分配页的线程上同步 kill（ledger 回调 `task_footprint_exceeded`，[XNU:task.c:1510]）——**超限者自己被自己的分配动作杀死，不污染系统**；
- 假 idle 检测：launchd 跟踪 daemon 被 kill 后的重启间隔（TTR），经 posix_spawn 属性传入内核，高重启率 daemon 标记 false-idle，触发 aggressive jetsam（可杀穿 foreground band）——**针对"杀不死的小强进程"的专杀机制**。

**Linux/Android**：

- 内核 OOM：badness 评分（oom_score_adj -1000..1000 用户可调），memcg OOM 有 oom.group（整组连坐，[L6.12:memcontrol.c:4242-4268]）；
- 历史：内核 lowmemorykiller 驱动已从上游 **4.12 移除**；官方迁移原因（[A17:lmkd]）：阈值硬编码且不随压力扩缩、厂商各自魔改、挂在 slab shrinker 上"搜目标+杀进程"拖慢整个 vmscan、活跃 page cache 负载下只抖不杀——**内核策略难以演进，是架构原因不是实现问题**；
- lmkd（用户态）：epoll 监听 PSI（`PSI_WINDOW_SIZE_MS 1000`、阈值 SOME 70/100ms、FULL 70ms，[LMKD:130-222]；官方参数化版本：`psi_partial_stall_ms` 70(高性能)/200(低内存)、`psi_complete_stall_ms` 700、`thrashing_limit` 100/30、`swap_free_low_percentage` 20/10，[A17:lmkd]）+ vmpressure 兼容路径 + swap free 水位 + minfree 阈值表；按 oom_score_adj 从大到小杀；进程冻结（cached app freezer）与杀的协调在 userspace；
- **Android 17 memory-limiter 系统服务**（[A17:memory-limiter]）：cgroup v2 按进程限额——`memory.high`（软限→节流+主动回收）+ **`memory.swap.max`（限制单进程 ZRAM/swap 用量）**，按可见性分档（16GB 设备：可见 10G / 不可见 5G / swap 5G）——**压缩内存开始进入 per-process 限额口径**，见 C4/C7；
- PSI 是 Linux 的独有优势：**时间维度的定量压力测量**（some=有人等内存，full=全员等内存），cgroup 级粒度，XNU 的 5 级枚举粒度上差一个数量级。

**差距实质**：

1. lmkd 的 kill reason 是"水位过了哪条线"，XNU 的 kill reason 是"系统哪里在生病"（文件缓存抖动/压缩器抖动/换出又读回/假 idle）——**归因深度完全不同**，前者只能事后猜（logcat 关联），后者直接写进崩溃报告；
2. oom_score_adj 是单调解的单调刻度，band 语义（含 aging band、freezer band、dock band）承载的是**生命周期状态机**，刻度承载不了状态；
3. Android 的 lmkd 本身仍在演进，缺的不是策略框架而是**内核侧的冷热/抖动反馈数据**（见 §4-C3）。

### 3.6 记账：phys_footprint 单一口径 vs 多口径并存

**XNU**（[XNU:task.c:1226-1233] 注释即公式）：

```
phys_footprint = (internal − alternate_accounting)           // 匿名脏页
               + (internal_compressed − alt_compressed)      // ★被压缩的页仍记你头上
               + iokit_mapped                                  // IOKit/DMA 映射
               + purgeable_nonvolatile (+ compressed)          // 只记"不可丢"的 purgeable
               + page_table                                    // ★页表也记账
```

- ledger 是记账原语：每 entry 可设限额+回调+峰值跟踪+每 60 秒峰值快照（[XNU:task.c:1457-1462]），soft/hard 限额、80% WARN、诊断阈值都建在它上面；
- IOKit 分类账：Network/Media/Graphics/Neural 四类可分别记账（IOMemoryDescriptor.h:126-131）——相机 932MB wired 归 Graphics，网络缓冲归 Network，**设备内存有科目**。

**Linux**：RSS/RSS_anon/swap/PSS 多口径；memcg memory.current/peak/events；**三层记账结构**（本会话梳理）：① swap entry 按原始大小 charge 到原 memcg（`CONFIG_MEMCG_SWAP=y`，lmkd 官方要求）——页被压进 zram 后从 memory.current 移到 memory.swap.current；② 压缩池的物理页（zsmalloc/zswap pool）charge 到 root——系统级"zram 占了多少 RAM"对 per-uid 不可见；③ zswap 自 v6.9 起把池内存记到 objcg（[L6.12:zswap.c:193-801]）并有 per-memcg shrinker（2026-08 又合入批量 writeback），zram 完全没有对应物。Android 17 用 `memory.swap.max` 给单进程 swap 用量设上限（[A17:memory-limiter]），把第①层纳入限额——**与 XNU 的 internal_compressed 记账方向一致**（XNU：压缩页按原始大小持续计入 phys_footprint 限额；Linux：按原始大小计入 swap 口径、上限单独设）。剩余差距在第②层：压缩池物理占用（真实吃掉的 RAM，≈原始大小×压缩比）无 per-process 归属，per-UID 限额与系统真实可用内存因此都存在偏差。

### 3.7 内核分配器（简）

XNU zalloc：每类型独立 zone + per-CPU 杂志 + `ZC_*` 安全标志（iOS 12→18 从 0 涨到 34 项，zone 加固演化）+ zone GC + **zone map 耗尽触发 jetsam**（kill reason 表第 9 项）；kalloc 统一 KHEAP。Linux SLUB：统一 cache 框架 + memcg kmem 记账（性能开销争议多年）+ shrinker 通用框架。值得对齐的一点：XNU 把"内核元数据内存不足"接到统一的查杀/回收框架上，Linux 的 zone/slab 耗尽路径分散在各子系统 WARN 里。

### 3.8 Android 17 收敛信号：Google 正在重新发明 XNU 的语义

调研期间从 Android 官方文档核验到的 Android 17（GKI 6.18）新机制（[A17:mmd / memory-limiter / cached-apps-freezer]），与 XNU 对应物逐条映射：

| Android 17 机制 | XNU 对应物 | 对齐度 |
|---|---|---|
| mmd：zram 日常维护守护进程，recompression 默认全开（退避 30min、idle 阈值 2h~4h 动态指数插值、≥1KiB 才重压） | 压缩器后台线程 + WKdm/LZ4 重压缩 | 形似：都做"冷了再压一遍" |
| mmd：writeback 每日预算 24GiB/天、单轮 ≤300MiB、单次 ≥5MiB | freezer 的 NAND 日预算（存储团队 IOCTL 下发） | **思路完全一致**（都把闪存寿命当一等约束） |
| memory-limiter：cached → frozen → 尽量回收 | freezer：挂起 → 全量压缩 → 换出 | 部分对齐（回收深度不同） |
| per-process zram writeback（对"杀了会被用户感知"的 cached app 定向换出，然后**提升 oom_score_adj**） | freezer band 75（换出后进保护 band，jetsam 不动它） | **语义等价**：换出者获得查杀豁免 |
| `ZRAM_ANDROID_IOC_PROCESS_PREFETCH` 解冻预取 | XNU 明确**不预取**（按需缺页拉回） | **方向相反**——两种设计各有取舍（见下） |
| `memory.swap.max` 限制单进程压缩内存 | `internal_compressed` 计入 phys_footprint 限额 | 方向一致（见 §3.6 三层记账） |
| 依赖 `CONFIG_ZRAM_TRACK_ENTRY_ACTIME`（GKI 6.18+ 默认开） | c_segment 逐段 c_creation_ts | 年龄基础设施，XNU 更完整（世代 + 目标年龄反推） |

三个尚存的本质差距，即 C1/C4 的增强空间：

1. **老化模型**：Android 17 用"空闲时钟"（idle 2-4h 指数插值），XNU 用"世代年龄 + 抖动反推"（`compute_swapout_target_age` 用解压回读率动态收紧阈值）——前者无法感知"换出后马上又被读回"的抖动，后者能；
2. **编排位置**：Android 17 全部在用户态（mmd/memory-limiter/JobScheduler，要求 idle+电量充足才维护），XNU 在内核事件驱动（压力即触发）——Android 的维护窗口可能错过压力时机；
3. **prefetch vs 按需**：解冻语义上两家做了相反选择，Android 换启动延迟可预测性，XNU 换 IO 带宽/功耗——无对错，但值得在各自生态里量化。

**方法论结论**：本文 §4 的优化方向有两条（C1/C4）已被 Android 17 部分落地——这**验证了**"XNU 语义是移动端内存管理的有效靶子"这一前提；剩余工作从"从无到有"变成"补齐差距"（世代老化、抖动反馈、内核事件驱动），难度显著降低。

---

## 4. Linux 优化方向（十条，两个目标）

**目标 A｜内存管理性能**：内存管理路径自身的吞吐、延迟与 CPU 成本——回收、压缩、换出、召回跑得有多快、多省。
**目标 B｜冷热页管理有效性**：冷热判定的准确性（谁真冷、谁被误判）、热集的保护（热页不被误逐）、冷页处置的正确性（放哪层、何时处置）。

选向逻辑：每条方向必须直接服务 A 或 B（zram 记账、kill 归因、页表记账类议题服务的是限额口径与归因，不属这两目标，已移出本节）；每条按【现状 → XNU 证据 → 方向 → 为什么选它 → 风险】展开，★ 为 Android 厂商可立即试点项。C 组（C1-C7）服务目标 B，P 组（P1-P3）服务目标 A。

### B 组：冷热页管理有效性

### C1 ★★ 世代年龄老化：冷判定从"空闲时钟"升级

- **现状**：MGLRU 已提供 per-memcg 世代时间戳（6.1+，PT-walk 采 access bit，成本远低于全量 rmap 扫描），但下游消费者几乎为零：zram 的 idle 检测是粗粒度访问时钟；Android 17 mmd 用"idle 2~4h 指数插值"的**空闲时钟**；writeback / recompression / 换出决策全部基于"多久没被碰"，而非"世代多老、多久不会再被碰"。
- **XNU 证据**：压缩段挂 `c_age_list` 按段年龄换出（`vm_ripe_target_age`），且 `compute_swapout_target_age` 用解压回读率**反推**目标年龄——判定标准随负载自校准（[XNU:vm_compressor.c:2160-2237]）。
- **方向**：把 MGLRU 世代时间戳批量导出为 zram slot 年龄（新增世代→slot 批量映射接口），作为 writeback / recompression / 换出的统一判定输入；世代年龄 + 访问频率双输入。
- **为什么选它**：冷判定是下游全部处置的**根输入**——判定错，压缩/换出/重压全链路白费；MGLRU 的数据已经在内存里，只差导出——增量成本最小、杠杆最大；C2-C7 与 P2/P3 的判定质量全部依赖此条。
- **风险**：世代→slot 批量映射接口需新增；MGLRU 世代粒度（数百 ms 级）对 zram slot 可能过细，需聚合。

### C2 ★★ 工作集保护：热集不被误逐

- **现状**：Linux 的防误逐机制刚起步且粗粒度：`min_ttl_ms`（6.1，[L6.1:vmscan.c:5381]）是**全局单值**——MGLRU 检测到工作集 refault 即停止逐出，但无 per-memcg 维度、无自动调参；`is_exec_file_folio` 保护可执行页 7.3-rc3 才合入；kswapd hopeless 检测（[L7.3:vmscan.c:7631-7642]）只识别"逐了也白逐"，不改变逐出决策。
- **XNU 证据**：哲学级对照——XNU 对匿名页**"压缩不驱逐"**：冷匿名页进 compressor（仍在 RAM，召回 ≈12µs/页），真正的驱逐只发生在 jetsam 杀进程时。热集存活由**机制**保证而非策略调参。Android 的 zram 本来同构（压缩不驱逐），但 writeback 一旦开启就有了"驱逐"路径——保护机制必须跟上。
- **方向**：`min_ttl_ms` per-memcg 化 + refault 率自动调参；writeback 决策增加"工作集 floor"约束（低于 floor 的 memcg 不换出）；exec folio 思路推广到常驻库/字体页。
- **为什么选它**：这是冷热管理的另一半——冷端处置做得再好，热端误伤一次就是用户可感知的卡顿（refault 是卡顿主因之一）；Android 17 writeback 量产后，这条从"理论"变成"必需"。
- **风险**：min_ttl_ms 的全局语义改造涉及 MGLRU 核心路径。

### C3 ★★ 抖动反馈环：冷热判定的自我修正

- **现状**：回收控制是**前馈**的（水位 → 目标页数）；PSI 回答"等多久了"，vmstat 回答"缺多少"，但没有信号回答"我刚才逐出的页是不是热的"。workingset refault 已有 per-memcg 数据但只做统计（[L6.1:workingset.c:40-132]）；zram 有解压回读计数但无"回读率"语义；MGLRU 世代直方图只在 debugfs（[L6.12:multigen_lru.rst:105-120]）。
- **XNU 证据**：三个专用反馈环——FC thrashing（phantom cache 换出-读回率）、VM compressor thrashing（10ms 窗口压缩+解压对数）、`compute_swapout_target_age`（回读率反推换出年龄）——**每个环直接改写判定参数**，不止报警。
- **方向**：zram 解压回读率 + refault（file/anon 分路）+ MGLRU 世代直方图 → 导出成 PSI 风格稳定接口 → 作为 C1 年龄阈值与 C2 工作集 floor 的**自动调参输入**。
- **为什么选它**：没有反馈环的冷热判定是开环控制——负载一变就失效；XNU 的实践证明反馈环是判定"长期有效"的必要条件；三个数据源全部已在内核，缺的只是语义化导出。
- **风险**：GKI 对导出接口的稳定性要求（debugfs 不够）。

### C4 ★★★ 冻结进程的冷页定向处置（最确定的冷源）

- **现状**：冻结/挂起 app 是"应用已声明冷"的最大冷源，但 Android 11~16 冻结零内存收益（只停 CPU）：8GB 设备 20 个后台 app × 300MB ≈ 6GB 死重。Android 17 管线（freeze → reclaim → per-process writeback → oom_score_adj 提升 → prefetch）已量产第一版，但三个差距未解决：空闲时钟 vs 世代年龄（C1）、用户态维护窗口 vs 内核事件驱动、无反馈环（C3）；且仅 GKI 6.18+。
- **XNU 证据**：freezer 管线——挂起 → 全量压缩 → NAND 换出 → band 75 查杀豁免；冻结后内存收益 ≈ 全量脏页（§3.3）。
- **方向**：(a) **存量回移**——Android 14~16 用 cgroup v2 + memory.reclaim（6.1+，swappiness 选项 6.12+）+ 全局 mark_idle/writeback 复刻同款管线（零内核改动）；(b) **超越**——接入 C1 世代年龄与 C3 反馈环；(c) ZRAM_ANDROID_* ioctl 上游化。
- **为什么选它**：收益最大且已被产业验证（Android 17）；冻结态 = 零假阳性的冷判定——唯一不需要"猜"的冷源。
- **风险**：thaw 召回风暴（见 P3）；闪存寿命（mmd 日预算已示范解法）。

### C5 ★★ 应用声明的冷内存：purgeable/volatile 语义

- **现状**：MADV_FREE 三个语义缺口——丢弃无序、无丢弃通知、记账不切换（[L6.12:madvise.c:642-782]）；ashmem unpin 的冷声明语义正随 ashmem 退役流失；图形缓存（SurfaceFlinger buffer、解码位图）是移动端最大的"可再生冷内存"。
- **XNU 证据**：purgeable token——应用声明 volatile 即冷：有序丢弃（FIFO/LIFO）、即时退出 footprint、丢弃可通知（[XNU:vm_purgeable.c:62-88]）；IOSurface/WebKit 全生态使用。
- **方向**：保守版——madv 扩展（丢弃顺序提示 + uevent 通知 + memcg 口径切换）；激进版——memfd volatile ioctl + gralloc/dmabuf heap 接入。
- **为什么选它**：应用最清楚哪块内存可再生（位图重解码即得）——声明式冷页是**零判定成本**的冷源，比内核猜冷热精确得多；iOS 证明该生态可行。
- **风险**：多进程共享 buffer 的记账归属。

### C6 ★ 区域级冷热识别：DAMON 产业化

- **现状**：MGLRU 是页粒度（access bit 采样 + PT-walk），对大地址空间（ART heap、浏览器）的冷热识别要逐页累积；DAMON 已有区域自适应聚合（物理/虚拟地址空间），`CONFIG_DAMON_PADDR` + `DAMON_RECLAIM` 在 Pixel 8 GKI 配置中已启用（gs201 存档实证），但**没有系统级编排消费它**。
- **XNU 证据**：无对应物——这是 Linux 领先项（§5 反向视角）；XNU 冷热识别全靠页级引用位 + compressor 段年龄。
- **方向**：DAMOS 方案接入 Android 内存编排——对 ART heap / 浏览器做区域级"先整体换压冷区域"；确立 DAMON（找区域）× MGLRU（执行逐出）的分工协议。
- **为什么选它**：区域采样的判定成本是页级的 1/N，而大内存进程正是 Android 内存压力的主力；机制在主线多年、配置已开，缺的只是消费方。
- **风险**：采样精度与区域聚合粒度的调参；生产可用性一手引用待补（附录 B 待核验④）。

### C7 ★★ 冷页分级存储：RAM → zram → NAND 的放置策略

- **现状**：zram 是单层"压缩即终点"；writeback 四模式（idle/huge/huge_idle/incompressible）是**模式开关而非放置决策**——没有"这个冷页该进 zram 还是直接进 NAND"的框架；不可压页先占 zram 槽位再等 writeback（incompressible 模式有了出口，但没有入口分流）。
- **XNU 证据**：c_segment 分级放置——热段留在 RAM 压缩、ripe 段整段进 NAND；不可压/大页有专门路径；换出以"段"为单位整体搬移（[XNU:vm_compressor.c:2160-2237]）。
- **方向**：放置决策框架（压缩收益 × 召回概率 × 闪存成本三输入）：不可压 → 跳过 zram 直写 NAND；huge → NAND；小而不常读 → zram；与 C1 年龄输入联动。
- **为什么选它**：分级存储是冷处置的最后一公里——放错层要么浪费 RAM（该去 NAND 的留在 zram）要么浪费闪存寿命（该留 zram 的写进 NAND）；Android 17 已备齐机制件（writeback 四模式 + per-process ioctl），缺的只是策略层。
- **风险**：决策模型调参复杂度；与 mmd 现有策略的兼容。

### A 组：内存管理性能

### P1 ★★ 大 folio 批量压缩/换出管线（吞吐）

- **现状**：zram 逐页压缩（per-page slot 分配/锁/中断开销）；7.3-rc3 已重构出 pp_slot 批量 writeback（`zram_writeback_slots`）——**上游自己正在走批量化路线**；zswap 大 folio swap-out 6.13 合入、swap-in（同步 IO）约 6.12（242d12c98174）；zram 大 folio 确证未合入（2022-04~2026-08 全部 200 提交核对）；Android 16K（Play 强制 2027-02-01，官方数据压力下启动 -3.16%）使批量化收益进一步放大。
- **XNU 证据**：c_segment 段打包（变长槽摊薄元数据）+ 16K 专用 WKdm 汇编内核；实测解压端到端 ≈12µs/页——吞吐根基就是"大页 + 打包"。
- **方向**：zram 大 folio 压缩（一次压缩 mTHP 尺寸 folio；zsmalloc 扩展大 size class）；沿 7.3 pp_slot 框架把批量化扩展到压缩路径。
- **为什么选它**：吞吐 = 冷处置的预算上限——压缩/换出越快，压力期处置越从容，越不需要 kill 止血；7.3 重构说明批量化的解空间已打开；16K 迁移使这条从"优化"变"必需"。
- **风险**：大 class 内部碎片；低端设备（4K 页 + 2GB）需按设备分级。

### P2 ★ 按页择优与重压调度（压缩性价比）

- **现状**：zram 一设备一算法；recompression（v6.2）已提供二次重压框架，Android 17 mmd 已做调度（idle 2-4h、≥1KiB 才重压、退避 30min）；但**首次压缩无按页分流**——稀疏零段页走完整压缩算法浪费 CPU，不可压页浪费一次完整压缩尝试。
- **XNU 证据**：metacompressor 四路分流——WKdm 常规页 / LZ4 大页(≥12KB) / 不可压原样存 / 单值页走 sv_hash（[XNU:vm_compressor.c:3872-3967]）；失败有负反馈学习（compressor_selector_update）。**真机教训**：默认 tuneables 让整个自适应休眠、实际 WKdm-only——自适应必须默认生效才有意义。
- **方向**：按页大小/采样熵预选 backend（lzo-rle → lz4 → zstd 梯度）；不可压页直走 incompressible 路径；重压调度接入 C1 年龄（只对"将长期冷"的页才投 zstd 的 CPU）；same_filled 从整页同值扩展到稀疏零段编码。
- **为什么选它**：压缩 CPU 是内存管理的隐性性能税——前台压缩抢核即卡顿，回收风暴时每页省下的微秒按百万页放大；XNU 四路中 Linux 已有两路（same_filled / incompressible），只差分流器。
- **风险**：zcomp 每算法 per-cpu stream 内存开销；低端设备收益可能为负——默认关、厂商按 SoC 定。

### P3 ★★ 冷页召回延迟（swap-in 路径性能）

- **现状**：zram 换入是单页读+解压；swap 层大 folio 换入刚起步（同步 IO 设备 242d12c98174 ≈6.12；`can_swapin_thp` v6.14 L4147；`swap_zeromap_batch` 零页批量）；zram 侧无大 folio 换入；Android 17 用 prefetch ioctl 对冲解冻延迟，XNU 选择按需缺页（零预取）——两个极端都缺量化数据。
- **XNU 证据**：解压端到端 ≈12µs/页（16K 真机）；NAND 换出页受 IO 支配；XNU 的取舍是**不预取**——省带宽/功耗，赌按需召回足够快（设计文档明言"just like macOS/iPad swap"）。
- **方向**：zram 大 folio 换入（与 P1 对偶）；swap readahead 与 mTHP 换入协同；prefetch 策略量化——以 C1 世代年龄预测召回窗口，定预取预算。
- **为什么选它**：冷热管理的全部收益最终以召回延迟结算——账单越大，C4/C7 越不敢做激进处置；换入路径批量化是当前空白且上游已铺路。
- **风险**：预取的带宽/功耗反噬；大 folio 换入对随机小访问的浪费。

---

## 5. 反向视角：Linux 已领先、值得 XNU 学的（保持结论的对称性）

1. **PSI 定量压力测量**：时间维度 × cgroup 粒度，XNU 的 5 级枚举 + 全局阈值无法与之相比。Android 把 PSI 作为 lmkd 的核心输入是正确路线。
2. **MGLRU 的 PT-walk + Bloom filter 反馈**（[L6.12:vmscan.c:4037-4042]）：老化成本从"扫全部"到"按线索访"，比 XNU 的引用位采样 + 队列假设更适应大内存。
3. **folio 化**：以 folio 为操作单元减少锁/TLB/元数据开销，XNU 仍逐页。
4. **可配置性与可追溯性**：min_ttl_ms / swappiness / memory.reclaim 全是运行时接口且有 git 史；XNU 的 tuneables 部分构建期固化（真机 WKdm-only 实证即恶果：开源默认与设备行为背离且无法在机上验证）。**"策略可运行时调整"本身是内存管理质量的组成部分。**
5. **机制/策略分离的生态收益**：lmkd 十年间从内核驱动改成用户态、反复迭代策略而不动内核（官方列举的迁移原因：内核版阈值硬编码、厂商魔改、shrinker 滥用拖慢 vmscan）；Android 17 的 mmd/memory-limiter 更是纯用户态就把 XNU freezer 式管线拼了出来——XNU 的同类演进必须跟着内核版本走。

因此 §4 的方向全部遵循同一原则：**学 XNU 的语义，用 Linux 的机制表达**（cgroup/MGLRU/zram 框架已备），避免把 XNU 的"一体化内核策略"整体搬入 Linux——那会推翻 Linux 十年建立的机制/策略分层。

---

## 6. 落地路线图

| 阶段 | 项 | 依赖 | 预期收益 |
|---|---|---|---|
| **短期（0-6 月，用户态/配置）** | C4(a)：在 Android 14~16 存量设备上复刻 mmd/memory-limiter 管线（cgroup v2 + memory.reclaim——6.1+ 可用，swappiness 选项需 6.12+——+ 全局 mark_idle/writeback）；C3 前半：回读率/refault 观测（tracepoint + debugfs 直方图，先观测后导出）；MGLRU 参数调优（min_ttl_ms 用起来）；确认 same-filled/recompression 开启 | 6.1+ 内核（Android 14 GKI 起） | 存量设备获得 Android 17 的主要内存收益；反馈数据开始积累 |
| **中期（6-18 月，产品内核补丁）** | C1 世代年龄接入（TRACK_ENTRY_ACTIME + MGLRU 时间戳映射）；C2 工作集 floor（min_ttl_ms per-memcg 化）；C7 放置策略层；C3 后半（PSI 式稳定接口）；P3 zram 大 folio 换入；C4(c) ioctl 上游化 | GKI vendor hook 或上游化 | 冷判定质量、热集保护、放置正确性、召回延迟 |
| **长期（框架级上游）** | C5 volatile API（memfd ioctl + 图形栈）；P1 zram 大 folio 压缩（等 RFC 重启）；C6 DAMOS 编排产业化（ART heap/浏览器区域级处置） | 主线 mm 社区共识 | 语义级对齐 XNU，生态级收益 |

---

## 附录 A：行号锚点与存档文件对照

### XNU（xnu-7195.141.2 = iOS 14.8，另有六版本对照存档）

| 锚点 | 存档文件 |
|---|---|
| vm_pageout.c:2730（pageout 内联 jetsam）、:10935-10987（水位+15%迟滞） | `skill/session-archive/xnu-7195.141.2_osfmk_vm_vm_pageout.c` |
| vm_compressor.c:2160-2237（compressor_needs_to_swap/ripe/thrashing）、:3872-3967（压缩预算链/SV/不可压） | `skill/multi-version-archive/xnu-7195.141.2_osfmk_vm_vm_compressor.c` |
| vm_compressor_algorithms.c:140-212（preselect/selector_update） | `skill/linux-vm-archive/xnu-7195.141.2_osfmk_vm_vm_compressor_algorithms.c` |
| task.c:1226-1233（phys_footprint 公式）、:1457-1462、:1510（ledger 回调） | `test1/xnu-verify/task.c` |
| vm_purgeable.c:62-88（token/available_for_purge） | `test1/xnu-verify/vm_purgeable.c` |
| kern_memorystatus.c:100-115（band 名表）、:307-308（aging band） | `skill/multi-version-archive/xnu-7195.141.2_bsd_kern_kern_memorystatus.c` |
| Apple 设计文档（doc/vm）：memorystatus.md / kills.md / freezer.md / pageout_scan.md / memorystatus_notify.md | `test1/xnu-verify/*.md` |
| lmkd.cpp（PSI 阈值/窗口常量） | `test1/xnu-verify/lmkd.cpp`（Android 12 时代版本） |

### Linux

| 锚点 | 存档文件 |
|---|---|
| 5.10 传统 LRU（page_referenced:850 等） | `skill/linux-vm-archive/linux-5.10_{vmscan,rmap}510.c` |
| 6.1 MGLRU（look_around:4589、min_ttl:4488/4525/5381）、madvise、workingset、freezer、zram | `skill/linux-vm-archive/linux-6.1_*` |
| 6.6 全套 mm | `test1/v6.6/mm/*` |
| 6.12（Android 16 基线）：vmscan（min_ttl:3995/5118、Bloom:4042）、madvise（lazyfree:642-782）、memcontrol（memory.reclaim:4290+、oom_group:4242）、zswap（objcg:193-801）、zram_drv（same_filled:205、recompress:1658/1804、writeback:588-647） | `skill/linux-vm-archive/linux-6.12_*` |
| 7.3-rc3（主线最新）：vmscan（exec folio:267、hopeless:7639、user_proactive_reclaim:7958-8124）、zram_drv（pp_slot:225、批量 writeback） | `skill/linux-vm-archive/linux-master_*` |

### 演化数据（skill 已六版本同口径验证）

- freezer 引用计数（两文件合计）：iOS 12→18 为 384→495→656→831→881→887（挂起语义持续加重）；
- sustained-pressure kill：iOS 16 新增（3 处）；
- swapout 引用：48→50→55→101→102→102（iOS 16 起翻倍——换出管线大改）；
- ZC_ zone 安全标志：0→0→21→33→34→34。

## 附录 B：核验记录与方法（网络受限下的替代方案）

本会话 web_search 不可用（无 API key），android.googlesource.com / source.android.com / lore.kernel.org 不可达。两条替代核验路线（原始底稿见配套文件 `linux-mm-android-research-notes.md`，含全部 URL 与逐项置信度标注）：

1. **逐版本单文件比对**（raw.githubusercontent.com，torvalds/linux 全版本 tag）：`grep -c` 关键符号在相邻版本的出现次数 → zswap objcg=v6.9（0→10）、exec folio=7.3（7.0/7.1/7.2 均 0）、memory.reclaim=v6.1（v6.1 memcontrol.c:6583 函数体核验）、min_ttl_ms sysfs=6.1（本仓 6.1 存档 L5381）；
2. **提交考古**（GitHub commit/compare API + kernelnewbies 版本页 + kernel.org releases.json + source.android.google.cn 官方文档镜像）：zram recompression=v6.2（84b33bf78889）、chained recompression ~7.0 移除（cedfa028b54e）、zram 大 folio 未合入（2022-04~2026-08 全部 200 提交核对）、zswap 大 folio swap-out=6.13、Android 17=mmd+memory-limiter 全套机制（官方文档页）、Android 16K Play 强制 2027-02-01。

**对调研底稿的两处勘误**（本会话复核后修正，底稿已同步标注）：
- min_ttl_ms 并非 v6.3 首现：7b8144e63d84（"section for working set protection"）是**文档提交**（+15 行 rst +4 行代码注释），min_ttl_ms sysfs 在 6.1 已存在（存档 L5381 为证）；
- memory.reclaim 合入版本确认为 v6.1（底稿原标 [记忆: 6.1]，本会话用 v6.1 源码核验通过）。

**主要来源**：kernel.org/releases.json；docs.kernel.org（multigen_lru / cgroup-v2 / zswap / damon / psi）；source.android.google.cn（/docs/core/perf/lmkd、mmd、memory-limiter、cgroups、cached-apps-freezer、binder-freezer；/architecture/kernel、16kb-page-size）；developer.android.google.cn（page-sizes）；kernelnewbies.org（Linux_6.2/6.8/6.13/6.15）；GitHub torvalds/linux commit/compare API（提交哈希均录于底稿）；实机配置 `gs201_gki_defconfig_lineage-21`（LineageOS 设备内核镜像，Pixel 8）。

**待核验清单**（后续有网络时补，按优先级）：① PTE memcg 记账（Rik van Riel 系列）是否已进主线及确切版本；② lmkd 2023-2025 的 per-UID memcg kill 路径增量（官方文档只写到 Android 11 策略层）；③ memory.reclaim 的 swappiness 选项确切合入版本（6.12 在位已证，更早未查）；④ DAMON 生产使用的一手引用（Google/Meta 博客、LPC 演讲）；⑤ mmd/memory-limiter 的 vendor ioctl（ZRAM_ANDROID_IOC_*）在 GKI 源码树中的实现位置（官方文档已证存在，源码不可达）。
