XNU vs Linux 内存管理全对比

Part I 回收流水线四层深潜 · Part II 按书 17 章逐一对照 · 每章判定谁优谁劣 · 实测与源码锚点支撑
对照基准:《iOS 内存管理深度剖析》v2.50 全部章节 iPhone 12 Pro (A14, iOS 14.8, 越狱) 实测 XNU xnu-7195.141.2 / Linux v5.10·v6.1 2026-08-30
EXP 设备实测 SRC 源码行号可查 EST 结构估算 GEN 通用知识(未逐行验证)

§TL;DR:17 章判定矩阵

一句话总判:XNU 的优势几乎全部来自集成(记账-查杀单币种、压缩器内嵌、freezer 整进程搬空、purgeable 一等公民),Linux 的优势几乎全部来自生态(VA 空间、观测工具链、文件系统协同、分配器与算法可插拔)。把 17 章逐一过堂后没有出现"一边倒":手机形态 XNU 占优 9 章,通用形态 Linux 占优 6 章,真正平手 2 章——而每一个胜负都能追溯到同一个分叉:苹果知道自己跑在哪,Linux 不知道自己会跑在哪。
主题XNU 选择Linux 选择判定
1总体架构三线一体(记账/回收/查杀)+256MB 极小 swap多子系统分立+swap 中心化各有胜场 哲学差
2页队列状态机双轨 inactive+throttled 隐藏队列+speculativeper-node×per-memcg LRU+MGLRU 世代平手 手机简/服务器隔离
3记账体系ledger+phys_footprint(查杀同口径)memcg(记账与 OOM 决策割裂)XNU 胜 单币种设计
4回收五阶段单线程+FCS 流控+jetsam 提前量kswapd+直接回收双路+水位阶梯手机 XNU 胜 延迟毛刺少
5真实场景相机 5.4GB 冲击:2s 回血+冻结保后台同压力走 zram+lmkd 杀后台体验 XNU 胜 内存利用 Linux 胜
6压缩器WKdm/LZ4 混合,VM 内嵌zram 块设备+算法可插拔集成vs生态
7NAND 寿命策略整体性(挂起+丢弃+小 swap)98% 吸收f2fs/dirty 节流/TRIM 文件系统协同Linux 胜 生态可调性
8Purgeabletoken 状态机+记账瞬移(一等公民)MADV_FREE 懒提示(仅匿名)XNU 完胜 语义代差
9Freezer整进程内存搬走(冻结=零占用)cgroup freezer 仅停 CPU(内存原样)XNU 能力独有
10Jetsam210 band+三级 aging 流水线(内核态)OOM badness+Android lmkd(用户态)手机 XNU 胜 通用 Linux 胜
11压力通知四级+滞回(面向 App 行动)PSI 连续量(面向系统调度)各有胜场
12分配器zalloc 安全起点→2020s 补性能SLUB 性能起点→2020s 补安全已趋同 书中原判
13限额双限额(前台宽/后台严)内核强制Android 框架层 memoryClass+lmkd语义 XNU 胜 通用 Linux 胜
14缺页/COW4GB VA 预算+影子对象 COW47bit VA+fork+rmapLinux 完胜 VA 空间
15观测接口vm_stat/task_info/Instruments 一致链/proc+tracepoint+eBPF 可编程Linux 完胜 观测生态
16vm_stat 字段free ⊇ speculative(经典陷阱)MemAvailable 自解释估算Linux 略胜 语义清晰
17测试落地repro-all.sh 一键复现stress-ng/drop_caches/psi 压测方法论互通

判定说明:胜负均限定场景(手机=单用户交互设备优先保前台体验;服务器/通用=多租户吞吐优先)。"能力独有"= 对方主线无对应机制。

PART I · 回收流水线四层深潜

§0两条回收流水线全景:四层对照

感知层(谁热谁冷) ├─ XNU : 陷阱式收割——老化清 AF 位设陷阱,下次访问撞 fault,当场记进物理页软件位 └─ Linux : 遍历式收割——rmap 反查(传统) / MGLRU 正扫页表(新一代),读 PTE 里的 A 位 老化层(冷热怎么分级) ├─ XNU : active/inactive 双队列,老化从属于回收(每轮回收最多搬 1 页预算、inactive 上限 50%) └─ Linux : 传统双队列(active/inactive) → MGLRU 世代化(编号代次,多次分级) + tier 防流式冲刷 执行层(冷页搬去哪) ├─ XNU : 四级阶梯——干净文件页白扔 / purgeable 没收 / freezer 冻结 / 压缩器(WKdm→LZ4 混合) └─ Linux : 干净文件页丢弃 / 页缓存回写 / zram 压缩换出(→ 可 writeback 到 flash) / 磁盘 swap 终端层(搬不动怎么办) ├─ XNU : 分配器阻塞(vm_page_wait) → jetsam 按位图整进程收割(内核态) └─ Linux : 直接回收(分配线程亲自下场) → OOM killer / Android: PSI 驱动用户态 lmkd
第一眼结论:四层里有三层结构同构(都丢干净页、都压缩、都杀进程),真正的分叉在感知层。其余三层的差异,多半是"集成 vs 设备化""内核态 vs 用户态"的工程形态差,不是能力差。

§1感知层:门铃的两种装法

1.1 XNU:拆门铃,等人撞门(REFFAULT 状态机)SRC

① 老化: arm_force_fast_fault (pmap.c:10419) 清每个 PTE 的 AF 位 + SET_REFFAULT_PAGE (:10645, PP_ATTR_REFFAULT=0x1000 :1068) ② 访问: 硬件见 AF=0 必卡门 (ARM ISA 强制, FSC=0x09/0A/0B, proc_reg.h:1492-1494) ③ 撞门: arm_fast_fault_internal (:10840) → arm_clear_fast_fault (:10704) 一次遍历该页全部 pv 项: 补 AF(WRITE_PTE_STRONG :10802) + 记 PP_ATTR_REFERENCED (:10768-10780) ④ 判定: 读一个软件位 → O(1) 复活判决

关键性质:ARM64 硬件页表遍历器没有笔——遇到 AF=0 不能自己补 1,只能罢工报警(access flag fault,FSC 0x09/0x0A/0x0B)。"AF 位被谁置 1"从头到尾只有软件一个答案(pmap.c:10802 的 WRITE_PTE_STRONG)。x86 上硬件代笔、内核无感知的难题被 ISA 直接消灭。

1.2 Linux:查岗式收割,两种姿势SRC

传统(v5.10 主线)MGLRU(v6.1+)
姿势反向:rmap 反查——page_referenced (rmap.c:850) → page_referenced_one (:767) → ptep_clear_flush_young_notify (:788),每页每映射一次正向:lru_gen_look_around (vmscan.c:4589)——按 VMA 批量正扫页表,一个 PMD 窗口 256 个 PTE 一批,MIN_LRU_BATCH=64 (mmzone.h:384)
成本O(映射数)/页:锁+树+四级页表走查+TLB flush,重共享页每页几μsEST每 PTE 常数,缓存友好(页表本身连续)
注意驱逐仍走 rmap(try_to_unmap, vmscan.c:1876)——正扫只取代了"判定",解映射的老本行没动

1.3 意外的实证:ARM Android 上门禁天天在响,但门卫不登记SRC

为什么不抄?可移植性——VM 核心必须跑 x86/RISC-V/PowerPC,判定绑死"卡门必报"等于绑死单一 ISA;② 粒度——Linux 决策吃映射级证据(referenced_ptes > 1 → 升 active,vmscan.c:1017);③ 已有解——MGLRU 批发正扫已把判定成本降到每 PTE 常数。同一扇门,XNU 当收发室,Linux 当普通门修——专用系统可以赌硬件,通用系统只能赌抽象。

§2老化层:从属式 vs 世代式

2.1 XNU:老化是回收的"学徒",没有自己的线程SRC

2.2 Linux:老化有自己的人生(kswapd + 世代推进)SRC

XNULinux(MGLRU)
分级粒度两级(active/inactive)+ 每轮二跳检查多代多档(世代×tier)
驱动方回收线程兼差(1:1 预算)kswapd 独立线程 + 直接回收兜底
空闲时整个机器停摆(不老化)kswapd 仍可被水位唤醒做后台整理
流式 IO 防护speculative 队列隔离SRCtier 沉底SRC
评价:多代分级的判别精度确实是 MGLRU 的真实优势。但精度换的是维护成本;XNU 的两级 + 压缩器兜底 + jetsam 强干预,在手机单前台模型下精度需求本就不高。

§3执行层:实测三档阶梯 vs Linux 的对应物

3.1 XNU 三档容量阶梯(iPhone 12 Pro 实测)EXP

档位吞吐带宽CPU瓶颈
稳态4,824 页/s75 MB/s~0.3%需求节奏(episode 结构)
爆发~13,000 页/s200 MB/s4.3%(0.26/6 核)流水线节奏(CPU 仅 26% 利用)
饱和50,010 页/s781 MB/s~1.0 核压缩机 CPU(≈单核 WKdm 774MB/s)

全路径回收成本 21.3μs/页(16KB),其中 WKdm 压缩 20.2μs95%)——判定+队列+摘映射合计 ≤1.1μs。爆发档交叉验证:19,200 页 × 20.2μs = 0.388 core·s 理论值 vs 0.387 实测。

3.2 压缩器内战:集成 vs 设备化

XNU:VM 内嵌混合压缩器SRC

  • metacompressor(vm_compressor_algorithms.c:296):WKdm 打头,结果 ≥2048B 或失败转 LZ4(lz4_threshold=2048);
  • preselect 状态机:LZ4 连续失败自适应跳过;
  • ARM popcount 指令加速 WKdm 字典哈希;16KB 页专用变体;
  • 压缩段与 VM 页表/队列同层集成——refault 路径零跨层。

Linux:zram 块设备生态GEN

  • zram = 内存里的压缩块设备,配 zsmalloc 紧凑分配器;
  • 算法可插拔:lz4(默认)/ zstd / 842…;
  • zram writeback:真冷页可再降到 flash——三级温度(RAM → 压缩 RAM → flash);
  • 同页去重(page_same_filled,zram_drv.c:210/:1356——零页/重复页白捡)SRC
  • 代价:swap 子系统跨块层,路径长于 XNU 内嵌压缩EST
对照判词:单看"压缩冷页"这一步,两家殊途同归(成本都由压缩算法主导)。差别在形态——XNU 是一辆整车(VM 自带),Linux 是乐高(块设备层拼装)。整车省路费(集成路径短),乐高换件快(算法/后端可换、能 writeback 分级)。

§4终端层:jetsam vs OOM/PSI——杀进程的三种姿势

XNU : 分配器阻塞 vm_page_wait (vm_resident.c:3173) → jetsam 内核态整进程收割 (按 210 个优先级 band 从低到高,aging 流水线做缓冲带) Linux : 直接回收 vmscan.c:3262(分配线程亲自下场,throttle :3149 节流) → OOM killer(内核态,badness 打分——服务器哲学:救系统,杀最肥) Android: PSI 计量 stall 时间 → 用户态 lmkd 按 oom_score_adj 收割——手机哲学:救体验,杀后台
XNU jetsamLinux OOMAndroid PSI+lmkd
决策位置内核态SRC内核态SRC用户态GEN
选择依据band 位图 + 前后台状态badness(内存占用×oom_score)PSI 压力 + oom_score_adj + 进程组策略
触发粒度提前量(压力前预杀后台)事后(OOM 才杀)连续量(按 stall 比例分档)
哲学厂商全栈:策略内核直调救系统不救体验策略外移:内核只计量,裁决归用户态
为什么 Linux 要把杀手搬去用户态?通用内核无法内置"哪个进程该死"的厂商策略——服务器要救 sshd,手机要杀后台抖音。PSI(v4.20)把"内存压力"变成可计量的连续量,lmkd 拿着这个数按厂商策略收割。XNU 不需要这层间接:苹果既写内核也定策略,jetsam 直接住内核里。这是"通用性税"最典型的一笔。

§5冷页四级阶梯:iOS 的"让贵的尽量不发生"

冷页类型XNU 处理每页成本Linux 对应物
① 干净文件页直接扔,将来从 flash 重读~0-1μsEST页缓存丢弃(同构)
② purgeable/volatile直接没收(nonvolatile 才保护)~0EXPMADV_FREE 懒释放(提示式)
③ 整进程冷态freezer 冻结 / jetsam 杀掉归零≈0(摊销)cgroup freezer(只停 CPU,内存不搬
④ 活进程脏匿名页压缩器压走20.2μsEXPzram 压缩换出

实测的 21.3μs 全路径成本压的是第④类——唯一没有免压缩通道的类。iOS 冷页策略的真正回答不是"把 20.2μs 变小"(编解码器已是 WKdm/LZ4 混合+硬件加速形态,实测贴着单核 WKdm 上限跑),而是让前三级免费通道吃掉绝大多数流量:与其花 2.5 秒压掉后台 app 的 200MB,不如零成本杀掉它整个归零。

§6Linux 优化策略专项识别

按"解决什么痛点 → 怎么做 → XNU 有无对应物/需不需要"三问过堂。结论:Linux 的优化清单几乎每一项都在补偿通用性,且大部分在 XNU 上要么不存在对应痛点,要么已有更便宜的解。

6.1 MGLRU 多代 LRU SRC——把"查岗"做成批发

6.2 PSI 压力计量 + 用户态 lmkd GEN——杀手外移

6.3 zram / zsmalloc / writeback 生态 GEN+SRC

6.4 水位阶梯 + kswapd/直接回收分工 SRC

6.5 workingset refault 检测 SRC——回收质量的反馈回路

6.6 memcg 作用域回收 / DAMON 采样 / 批量 folio GEN

§7Part I 收束:一段哲学

两家用同一条物理定律写出两套风格迥异的实现,差异全部可以从一句话推出——苹果知道自己跑在哪(一种 ISA、一种负载模型、一个厂商全栈),Linux 不知道自己会跑在哪(x86 服务器到手表)。所以 XNU 敢把判定建立在 AF fault 这种 ISA 礼物上、敢把杀手放进内核、敢让老化无线程从属回收;Linux 必须把每样东西做成可移植、可插拔、可由发行版/厂商外置决策——MGLRU、PSI、zram、lmkd 每一项"优化"的账单背面,都印着"通用性"三个字。而两家正在收敛:Linux 学苹果做压缩内存(zram 成为 Android 默认)与整进程治理,苹果学 Linux 做世代化分级与内存压力量化。殊途,同归——因为物理定律只有一条。
PART II · 按书 17 章逐一对照

以下每章三段式:书里怎么讲(含书内锚点)→ Linux 对应物(含源码锚点/分级标签)→ 判词。与 Part I 重叠的章(2/4/6)只讲增量,不重复深潜内容。

书§1总体架构:三线一体 vs 多子系统分立

书里怎么讲

iOS 内存管理的骨架是三条线拧成的单股绳:记账线(每物理页归属 task 的 ledger,phys_footprint 就是查杀口径)、回收线(vm_pageout_scan 偷页,不征求进程同意)、查杀线(memorystatus 按 210 band 收割 + freezer 免死通道)SRC。真机实测修正了"iOS 无 swap"的流传说法:6GB 机型上 vm.swapusage 显示 256MB 加密 swap 真实存在,压力期观测 8613 次 swapout(134MB 落盘)——角色是压缩器之后的第二级小号泄压阀(仅占 6GB 的 4%),而非桌面式平等换页层EXP

Linux 对应物

Linux 是多子系统联邦:swap 子系统(平等的换页层,可多设备、可加密、可优先级)、cgroup/memcg(记账与限额)、vmscan(回收)、OOM killer(查杀)——各自独立演进、独立配置。swap 在 Linux 是一等公民:桌面/服务器可以 swappiness=60 常态换页,Android 用 zram 造出内存 swap。GEN

XNULinux
swap 地位刻意极小(4%),泄压阀而非层EXP一等公民,可配置成主力
子系统耦合三线一体(记账=查杀口径)联邦制(记账/回收/查杀各自为政)
默认行为压缩优先、杀进程兜底按配置漂移(发行版决定人格)
判词:各有胜场(哲学差)。XNU 的单股绳让"内存不足"这一个信号在三线间无损传递——这是后面 §3(记账)、§10(查杀)两章 XNU 胜利的结构性原因。Linux 的联邦制让同一内核能扮服务器/手机/超算——这是 §7/§14/§15 Linux 胜利的结构性原因。架构层没有胜负,只有取舍。

书§2页队列状态机:双轨+隐藏队列 vs 分层 LRU

书里怎么讲

页的 vmp_q_state 枚举是全书钥匙SRC:free 三道水位(min/target/absolute)、active、双轨 inactive(11=internal 匿名/12=external 文件——"2:1 比例"等回收策略的基础)、throttled 隐藏队列(不换页时匿名页暂存于此,对扫描不可见)、speculative(带保护期的投机页)。老化由 vm_page_balance_inactive 以 1:1 预算从属于回收,inactive 目标为可用页的 1/2(vm_pageout.c:227)。

Linux 对应物

LRU 按 node × memcg × (anon/file) × (active/inactive) 多维展开,外加 unevictable 链(mlocked 页);MGLRU 后变成 per-memcg 世代序列SRC。"throttled 隐藏队列"的 Linux 对应物是 sc->may_swap/swappiness 开关:swap 不可用时直接不扫匿名(而非藏起来)GEN。speculative 的对应物是 page cache 的 workingset 标记与 refault 距离保护SRC

XNULinux
队列数量级全局一套双轨(简单)node×memcg×4 起(隔离强)
匿名/文件分流两条 inactive 队列物理分开两条 LRU 物理分开(同构!)
swap 不可用时匿名页藏进 throttled 队列调低扫描倾向(may_swap)
投机/保护期speculative 队列+保护期workingset+refault 距离
判词:平手(场景决定)。双轨 inactive 两家惊人同构——匿名/文件分流是共同教训。XNU 的全局单套队列在单用户手机上是优点(无跨 memcg 公平性难题);Linux 的分层 LRU 在多租户下是刚需。throttled 队列是 iOS 弱 swap 的特化产物,语义比 Linux 的"扫描倾向"开关更硬。

书§3记账体系:ledger 单币种 vs memcg 记账与决策割裂

书里怎么讲

每个物理页归属一个 task 的 ledger 账户(internal/external/purgeable…),记账时机 = 页插入 pmap 的瞬间;jetsam 判死用的 phys_footprint = RSS + 压缩器占用 + IOKit/交替映射 + purgeable nonvolatile——不含 clean 文件页(可丢,杀了也释放不出)。书里明确建议 Linux 开发者把 ledger 类比 cgroup memory.stat,但记账时机不同SRC

Linux 对应物

memcg 在分配/缺页时 charge 到 cgroup:memory.current = anon + page cache + kernel(slab 等)+swap。两个口径差:① page cache 计入 memory.current 但可随时 reclaim——账面虚高;② OOM 决策还要另算 oom_score(进程维度),与 cgroup 账本(组维度)经常打架GEN

XNU ledgerLinux memcg
记账粒度per-task(进程)per-cgroup(组,进程须另行读取)
记账时机页进 pmap 瞬间分配/缺页时
含 page cache?不含 clean(口径=可杀释放量)含(口径=占用总量)
与查杀关系同一数字(footprint)需换算(oom_score 另算)
判词:XNU 胜(单币种设计)。这是全书最干净的一场胜利:XNU 的账本数字直接就是查杀决策数字——"phys_footprint 超限"与"杀它能释放这么多"是同一句话。memcg 的账混着可回收页,OOM 决策还要二次换算,记账与决策天然割裂。代价:XNU 没有组粒度(多租户记账弱,macOS 服务器化才会痛)。

书§4回收五阶段:单线程流控 vs 双路回收

书里怎么讲

vm_pageout_scan 的五阶段:初始化速赢(pmap 层归还多余页)→ 队列平衡与锁管理 → 流量控制状态机(FCS)→ 挑选受害者(顺序是精华)→ 处置。机制常驻、劳动按需:扫描线程平时睡,水位跌破才醒SRC。真机差分实测(10 秒窗口)证实空闲期扫描计数零增长EXP。压力全链路行为见 Part I §3 三档阶梯。

Linux 对应物

双路回收:kswapd(每 node 一线程,low 水位唤醒回补到 high)+ 直接回收(分配线程在 min 水位下亲自下场回收,SWAP_CLUSTER_MAX=32 页一批,vmscan.c:1939/:3262SRC)+ throttle_direct_reclaim 节流(:3149)。MGLRU 世代推进替代传统 list 扫描SRC

XNULinux
回收主体单线程(+分配者阻塞等待)kswapd 后台 + 分配线程前台双路
过载时分配者阻塞在 vm_page_wait(统一排队)SRC自己下场回收(直接回收,延迟自付)
流控FCS 状态机(全速/限速/让位)水位阶梯+节流+congestion
兜底jetsam 提前量(不等回收失败)OOM(等回收真失败才杀)
判词:手机场景 XNU 胜。关键差在过载时刻的行为:XNU 让分配者排队、系统统一调度、提前杀后台保前台——前台延迟毛刺小;Linux 让分配线程自己回收,回收成本直接打进业务延迟(Android 卡顿的经典来源之一,PSI/lmkd 就是为补救这个而生)。但服务器场景反转:直接回收让业务"用延迟换内存"而非被杀,保吞吐——这正是 Linux 哲学。

书§5真实场景(相机冲击 5.4GB):冻结保后台 vs 杀后台

书里怎么讲

三幕实测EXP:① 冷启动相机分配 1.36GB——回收系统纹丝不动(free 从 133,376 页砸到 46,930,警戒线 31MB 外全是余量,扫描器没醒);② 追加 2GB 高熵不可压——free 一度砸到 2,860 页(距 min 一线之遥),但 2 秒内 765MB 匿名页送压缩、126MB 投机页直接丢,free 弹回 1.2GB——全程压力级别 1、零进程死亡;③ 幕后四个后台 App 被整体冻结(freezer 收编,不杀不占)。

Linux(Android)同场景推演

① 同构——free 池直接吸收(Android 冷启动相机同样主要吃 page cache/free);② zram 压缩匿名页 + lmkd 按 PSI 分档杀后台——大概率有后台进程死亡(cached app 首当其冲);③ cgroup freezer 只停 CPU,内存不释放,压力真大时 Android 的答案就是杀GENEST

判词:体验 XNU 胜,内存利用率 Android 胜。iOS 的 freezer 把"后台保命"做成了架构能力(第三幕是全剧真正主角);Android 的"杀后台+快速重启"路线内存周转更彻底(杀掉=100% 释放 vs 冻结=压缩后仍占 ~20-30%EST),代价是切回后台 App 要重载。两套都是自洽的用户体验经济学,取向相反。

书§6压缩器:c_segment 整车 vs zram 乐高

书里怎么讲

数据结构是 c_segment:压缩页按 16KB 粒度打包进段(段自身是压缩池的分配单位),slot 记录 (codec, offset, popcount)。压缩主路径带混合策略:WKdm 打头,≥2048B 或失败转 LZ4(metacompressor,lz4_threshold=2048,vm_compressor_algorithms.c:296SRC);ARM popcount 硬件加速;16KB 页专用变体;per-CPU scratch 免锁。解压侧对称(popcount 快速校验)。实测 20.2μs/页、774MB/s 单核贴顶EXP

Linux 对应物

zram 块设备 + zsmalloc(size class 紧凑分配,功能对应 c_segment 的 slot 打包)+ 算法可插拔(lz4/zstd/842)+ 同页去重(page_same_filled,zram_drv.c:210/:1356——全零/重复页一个 element 就存,白捡SRC)+ writeback 三级温度。解压走块设备读路径(更长的调用链EST)。

XNU 压缩器zram 生态
位置VM 内嵌(refault 零跨层)块设备层(跨 swap 子系统)
算法WKdm/LZ4 混合+自适应状态机可插拔(zstd 压缩比更高可选)
同页去重无(zero-filled 有单独快路径)(same_filled)
第三级温度无(冷到底靠 jetsam)(writeback 到 flash)
加密段级(休眠/整体加密体系内)依赖上层(dm-crypt 等)
判词:集成 vs 生态(延续 Part I §3.2)。XNU 赢在路径短与自适应混合(工程打磨度高);zram 赢在可插拔、去重、writeback 三级温度(能力宽度)。iOS 的 256MB swap 其实就是"writeback 的极小版"——苹果做了同构但刻意缩量,因为 NAND 寿命账(书§7)不允许更大。

书§7NAND 寿命管控:策略整体性 vs 文件系统协同

书里怎么讲

三层防线SRC+EXP:第 0 层 NAND 硬件合同(固件磨损均衡+7320 备用块池,ioreg 实测 Toshiba TLC);第 1 层内核写入节流(98% 写入吸收率——压缩器优先让脏页不落盘、限流队列控制换页 IO、swap 刻意极小);第 2 层系统策略兜底(挂起进程零脏页——"不跑的进程不产生脏页"、purgeable 丢弃走重算不走闪存、墓碑 5 具上限)。结论:6GB RAM+TLC 组合获得十年级寿命预期。

Linux 对应物

writeback 节流:balance_dirty_pages 按比例(dirty_ratio/dirty_background_ratio)限脏页总量,BDI 感知设备速度GEN;② 文件系统协同:f2fs 专为 flash 设计(日志结构、SSR、分段 GC),ext4 discard/TRIM 定期回收块GEN;③ swappiness 可调低护 flash;④ Android 同样小 swap + zram 减写。

XNULinux
节流位置VM 层(限流队列+压缩器优先+挂起)块层+文件系统层(writeback+f2fs)
可调性基本不可调(策略焊死)dirty_ratio/swappiness/挂载选项全开放
独特武器挂起模型(后台零脏页)f2fs(文件系统级磨损感知)
实测锚点98% 吸收率EXP无本会话实测GEN
判词:Linux 胜(生态可调性)。Linux 把寿命管控拆到文件系统/块层做成了可组合工具箱,f2fs 是 XNU 没有的专门武器;XNU 的王牌"挂起零脏页"赢在系统模型而非单点技术,且不可移植(服务器没有"挂起后台"概念)。公平地说:两边在手机形态下都活下来了(iPhone 与 Android 的 NAND 都不是短板),说明两条路都及格——但工具箱的上限更高。

书§8Purgeable 内存:一等公民 vs 懒提示

书里怎么讲

四状态 token 状态机(VOLATILE/NONVOLATILE/EMPTY/FROZEN)+ 成熟(ripe)分级:丢弃顺序按 ripe 程度遍历(vm_purgeable_purge_object,:821-1000SRC)。最妙的是记账切换:进入 VOLATILE 瞬间 footprint 账面瞬移(PurgeableNonvolatile→PurgeableVolatile 账户间划转)——App 的内存压力评分立刻下降,"我可以被丢弃"从口头承诺变成账本事实SRC。实测:64MB nonvolatile 触摸后计数仅 +123(只统计 VOLATILE 态),EMPTY 后 purges +4,102≈4,096 ✓EXP

Linux 对应物

MADV_FREE(madvise.c:743 起的路径——仅对匿名 VMA 有效,标记可懒释放:回收时直接丢页不改盘,但不改变任何记账,下一次写会把标记悄悄撤销)SRC;Android 历史上的 ashmem(pin/unpin 语义几乎就是 purgeable 的翻版,后被 memfd 取代弱化)GEN

purgeableMADV_FREE/ashmem
语义强度契约(状态机+token 持有量)提示(内核可不理会)
记账联动VOLATILE 瞬移(footprint 立降)无(账面不变)
丢弃粒度按 token 成熟度有序无序(LRU 顺带)
数据可恢复性FROZEN 态可带内容挂起无对应
判词:XNU 完胜(语义代差)。这是 17 章里差距最悬殊的一场:XNU 把"可丢弃内存"做成了与记账/查杀联动的第一等 API——App 交出 purgeable 内存的瞬间, jetsam 对它的死亡判定就放松了;Linux 的 MADV_FREE 只是回收时的顺带优化,App 与内存压力体系之间没有这种"谈判通道"。ashmem 证明 Android 曾想要同样东西,但主线上始终没长出来。

书§9Freezer:整进程搬空 vs 只停 CPU

书里怎么讲

先分清概念:挂起(suspend)是"人不动了",冻结(freeze)是"家也搬走了"——freeze_top_process 只挑已挂起进程(freezer.md:43SRC),把整进程的(脏)内存页送压缩器/swap,进程在内存账上近乎清零;解冻时按需解压回。相机实验第三幕的四个后台 App 就是这条通道(书§5)。

Linux 对应物

cgroup freezer:__refrigerator 是一个 set_current_state(TASK_FROZEN); schedule(); 循环(kernel/freezer.c:62-90SRC)——只是让任务睡在调度器里,内存一页不动。Android 11+ 用它冻 cached app 的 CPU(省电),但内存压力来临时冻结帮不上忙,还得 lmkd 杀。内核态"整进程换出再挂起"在主线 Linux 无对应物GEN

XNU freezercgroup freezer
冻结时 CPU
冻结时内存搬走(压缩/落盘,账面清零)原样驻留
内存压力贡献≈整进程 footprint×(1-压缩比) 立即释放
解冻成本解压回读(延迟秒级可见)唤醒即可(零成本)
主力场景内存压力+后台保命省电/快照(S4 休眠另算)
判词:XNU 能力独有。这是 XNU 在 Linux 主线上没有对应物的两项能力之一(另一项是 §8 的记账联动)。"冻结=零占用"让 iOS 敢同时养几十个后台 App 而不怕内存崩盘——Android 要等 app hibernation/RAM 压缩这类机制逐步补课。Linux 的版本赢在解冻零成本(不搬内存所以不用搬回来),但那是另一道题的答案。

书§10Jetsam:210 band 流水线 vs OOM/lmkd

书里怎么讲

210 个优先级 band 从 idle=0 到 critical,查杀永远从 band 低端开始SRC。设计精华是三级 aging 流水线:退后台→AGING_BAND1(10)(10 秒防"刚切出就被杀",:412)→若持 importance assertion 转去 stuck(15)(再宽限 30 秒,:413;"相信进程的自我声明一次,但不相信第二次")→idle=0。aging band 整体低于 ELEVATED_INACTIVE(40) 由 assert 强制(数据结构层面的关注点分离);macOS 上三级 aging 全归零(桌面无"后台即死刑"压力)。

Linux 对应物

两条线:服务器线 OOM killer(badness=内存占用×oom_score,事后型——真 OOM 才动手,救系统不救体验)SRC;Android 线用户态 lmkd(PSI 驱动+oom_score_adj 1000 档,提前型)。Android 侧也有 band 思想(foreground/visible/cached 的 adj 梯度)与 aging 思想(cached 进程按 LRU 逐级加压)GEN

jetsamLinux OOMAndroid lmkd
band 数量210(细粒度)badness 连续分adj 1000 档
缓冲带10s→30s 两级 aging(语义精确)无(一击必杀)有(隐式,按 PSI 梯度)
提前量有(水位+band 双触发)有(PSI 分档)
延迟内核态直接收割(快)内核态(快但迟)用户态轮转(多一跳)
判词:手机场景 XNU 胜;通用场景 Linux 胜。10s/30s 两级 aging 的设计精度("用户切换注意力的最小典型时长")是产品工程内嵌进内核的典范,lmkd 的梯度是后验补课。但 lmkd 的用户态裁决让每个厂商能自定义生死簿——jetsam 的策略焊死在内核里,只有苹果能改。又是集成与生态的老对立。

书§11压力通知:四级+滞回 vs PSI 连续量

书里怎么讲

四级(normal/warn/critical/critical-系统自身)+ 滞回设计:上升与下降阈值是两根不同的线(memorystatus_notify.md:60-73SRC)。设计围绕一个矛盾:内核水位连续波动,但 App 只认得懂离散级别且每级绑定明确动作(级别即催促强度)。kevent → 用户态 → didReceiveMemoryWarning 链路。

Linux 对应物

PSI(v4.20):把"因内存而停顿"计量成 some/full 两口径×avg10/60/300 三窗口的连续量,/proc/pressure/memory 可读可 poll 可配阈值触发GEN;旧 vmpressure(cgroup 三级 low/medium/critical)是 XNU 四级的同族GEN

四级+滞回PSI
信号形态离散 4 级(面向 App 行动)连续百分比(面向调度/告警)
抗抖动显式滞回(双阈值)窗口平均(天然惯性)
消费方App(didReceiveMemoryWarning)运维/lmkd/调度器
测量对象水位派生量停顿时间(更接近用户体验)
判词:各有胜场(服务对象不同)。XNU 的离散级+滞回是"给 App 的协议"——每级对应明确行动建议,把抗抖动做成显式契约;PSI 的连续停顿计量是"给系统的仪表"——测量口径本身(stall 时间)比水位派生量更贴近真实痛苦,可编程消费。互相学一半正好:Android 给 App 的 onTrimMemory 也是离散级,Linux 服务器的低水位通知也在学滞回。

书§12分配器:安全起点 vs 性能起点(书内已判)

书里怎么讲

书里已有完整横向对比(此处引用+补足):zalloc 每类型一个 zone(292 个实测),隔离目的=安全缓解(四选项默认全开:SEQUESTER VA 隔离/SUBMAP 数据隔离/KEXT 圈禁/STRICT_IOKIT_FREE,zalloc.c:476-493SRC);SLUB 的 per-CPU 缓存为性能而生。2020 年代两者趋同:zalloc 补了 per-CPU 缓存(zpc),SLUB 补了随机化 freelist。终点一致:类型隔离 × per-CPU 私有 × 防利用加固。

Linux 对应物(用户态补足)

内核侧书上已对齐。用户态:XNU 侧 libmalloc(nano/tensor 区域)+ scudo(安全优先,MTE 协同)——受 4GB VA 预算约束被迫缩设计(33 类×256MB≈8.4GB 主区装不下,书§12.3 实测EXP);Linux 侧 ptmalloc/tcmalloc/jemalloc 百家争鸣,47bit VA 随便铺GEN

zalloc+libmalloc/scudoSLUB+ptmalloc 等
内核隔离哲学安全起点(UAF 不跨类型)性能起点(per-CPU 快路径)
2020s 演化补性能(zpc)补安全(freelist 随机化)
用户态生态官方统一(scudo 强制安全)可换装(tcmalloc/jemalloc 各有场景)
约束4GB VA 预算挤压设计EXP47bit VA 自由
判词:内核侧已趋同(书内原判);用户态 Linux 生态胜。内核分配器这场"殊途同归"是全书少有的明确收敛案例——两边把对方的优点都抄完了。用户态分配器 Linux 的可换装生态(性能敏感用 jemalloc、稳定用 ptmalloc)对服务器是真实优势;iOS 的 scudo 强制安全是手机威胁模型的正确答案,但没有选择权。

书§13限额与 entitlement:双限额 vs 框架层限额

书里怎么讲

双限额机制: JetsamProperties 给每进程两套 footprint 限额——active(前台宽)/inactive(后台严,显著更低),进程状态切换时内核写入 ledger(kern_memorystatus.c:1310-1326SRC)。entitlement 是突破默认限额的唯一正规通道:iOS 13 extended entitlement 可在 2GB 级设备抬到 1800MB(:418,"almost 2GB"不是修辞——给系统自身留不可压缩开销)。iOS 用生命周期管内存(前台/后台两套规则),macOS 用水位管内存——同一内核两种人格。

Linux 对应物

Android:ActivityManager 的 memoryClass/largeHeapClass 是框架层约定(App 申请大堆走 manifest),内核不知道这事;执行靠 lmkd 按 adj 梯度杀GEN。服务器:cgroup memory.max 是组限额硬约束(内核态执行,但无前台/后台语义)GEN

双限额+entitlementmemoryClass / cgroup
执行层内核态(限额写入 ledger,超限即查杀依据)框架层(Android)/内核态(cgroup)
前后台语义(active/inactive 两套)无(adj 梯度近似)
豁免通道entitlement(审核留痕)largeHeap 标志/调整 memory.max
语义精度高(生命周期感知)低-中(静态)
判词:语义精度 XNU 胜;工程通用性 Linux 胜。"前台宽、后台严"的双限额把用户行为模型编译进了内核约束——Android 的 adj 梯度+lmkd 是同思想的用户态粗糙版。但 cgroup memory.max 能给任意进程组画线(CI 任务、容器、数据库),这种任意分组能力 XNU 的 per-task 模型给不了。

书§14缺页与 COW:4GB VA 预算 vs 47bit 自由

书里怎么讲

真机实测:普通进程 mmap 保留 4GB(1<<32)成功、8GB 失败——iOS 给进程的 VA 预算远小于桌面/LinuxEXP。这不是物理限制而是内核对 VM map 的主动约束,直接后果:scudo 的 8.4GB 主区设计装不下必须缩;稀疏数据结构按 4GB 设计。COW 用影子对象:fork 共享 VM object,子 map 标只读,写→缺页→逐页复制。实测 COW 粒度精确到 1 页(16KB):子进程写 1 页 RSS +1 页EXP

Linux 对应物

47bit(256TB)VA 随便保留(MAP_NORESERVE 走起);COW 用 fork 的 dup_mmap 复制 VMA+PTE 置只读,写→缺页→复制页+rmap 重挂。语义完全等价,但实现路径不同:Linux fork 复制页表(页多则 fork 慢,故有 vfork/posix_spawn 优化);XNU 影子对象延迟复制(fork 快,读路径多一层间接)GEN

XNULinux
VA 预算4GB/进程(刻意约束)256TB/进程(自由)
COW 语义影子对象,逐页复制(同)dup_mmap+rmap,逐页复制(同)
fork 大空间成本低(影子延迟)页表复制随页数涨
大地址空间程序被迫缩设计随便跑
判词:Linux 完胜(VA 空间)。COW 语义两家等价(都精确到页,实测互证),但 VA 预算是碾压级差距。XNU 的 4GB 是安全沙盒的刻意选择(压缩攻击面+审计简单),代价是大型程序设计被迫迁就——数据库、JVM 堆、稀疏索引在这条线上全是受害者。Linux 的 256TB 是给一切可能性的通行证。

书§15观测接口:一致但封闭 vs 开放可编程

书里怎么讲

XNU 观测链:sysctl(模拟器/macOS 可读,真机受限)、task_info/task_events_info 进程级 API、mach_zone_info 枚举 zone、Instruments(VM Tracker 的 Dirty/Compressed/Clean 分区与 §3 账户对齐)、JetsamEvent-*.ips(reason+30s 快照)、vmmap/leaks/heap。配套 repro-all.sh 一键复现全书实验+CHECKSUM 校验SRC

Linux 对应物

/proc/meminfo、/proc/pid/smaps(逐 VMA RSS/PSS/匿名/swap 分解——精细度超过 vmmap)、/proc/pid/status、vmstat/slabtop、tracepoint+kprobe、eBPF(任意内核事件的可编程观测——给 bcc/bpftrace 写脚本实时聚合)、perf。GEN

XNU 链Linux 链
一致性(Instruments/账本/查杀同口径)弱(各工具口径要自己对齐)
精细度中(官方给多少看多少)(smaps 逐 VMA、BPF 任意事件)
可编程性无(dtrace 受限)eBPF 图灵完备
闭环观测→调优→复现一体(repro 脚本)工具多但组合靠人
判词:Linux 完胜(观测生态)。这是 17 章里 Linux 优势最大的一场:eBPF 让"我想知道内核任何函数任何时刻的行为"成为写脚本五分钟的事——XNU 上同等问题要么等 Apple 加 API、要么越狱读内核结构。XNU 唯一的反击点是口径一致性:phys_footprint 一个数字贯穿账本/查杀/Instruments,不会出现 Linux 那边 RSS/PSS/oom_score 各说各话的口径对齐成本。

书§16vm_stat 22 字段 vs /proc/meminfo

书里怎么讲

三总纲:① 水位类 vs 累计类是两种问题("事件已发生"vs"内存还占着");② free ⊇ speculative(host.c:815:free_count=vm_page_free_count+speculative_count——"Pages free"含投机页,严格水位分析要 free−speculative)SRC;③ 平台分支陷阱(iOS 的 wire_count 只含 vm_page_wire_count,active 含 per-CPU 本地队列,fb/anon 是 pageable 口径不含 wired)。22 字段逐一给了填充点/增减点/实测验证EXP

Linux 对应物

/proc/meminfo:MemFree 不含 page cache(脏净都不含);MemAvailable 是内核估算的"再不换页的前提下还能给多少"(把可回收 cache、可换出 anon 按水位折算)——语义上等价于 XNU 的"free+speculative 的诚实版"。GEN

vm_stat/proc/meminfo
"可用内存"字段Pages free(投机页——陷阱)MemAvailable(自解释估算)
陷阱程度高(free 语义被重载)低(MemFree/MemAvailable 分工明确)
累计计数11 个(faults/COW/zero-fill/压缩…)分散在 /proc/vmstat(pgfault/pgmajfault…)
平台一致性iOS/macOS 字段口径有分支差异跨架构一致
判词:Linux 略胜(语义清晰度)。两家都在"free 该不该含可回收物"上挣扎过——XNU 选择把 speculative 塞进 free(与 jetsam 口径一致但坑观测者),Linux 分成 MemFree/MemAvailable 两个诚实字段。XNU 的反击:vm_stat 的 11 个累计字段(尤其压缩/解压计数)直接对齐压缩器内部,Linux 想看 zram 命中率要另行读 /sys/block/zram0/mm_stat。

书§17测试落地:方法论互通

书里怎么讲

测量矩阵(已验证的实验直接复用)+ 压测脚本三个常见错误(累计/水位混读、采样自身污染、episode 边界误判)+ repro-all.sh 一键复现 + audit.py 14 项自动验算SRC

Linux 对应物

stress-ng(内存/缺页/COW/swap 全模式压测)、/proc/sys/vm/drop_caches(清页缓存——注意 XNU 无直接等价,投机页要走保护期)、PSI 阈值压测、vm.min_free_kbytes 水位调参实验GEN

判词:方法论互通(不算技术胜负)。书的"累计/水位分离、采样自污染、episode 边界"三原则在 Linux 侧同样成立(drop_caches 后 MemFree 立涨是水位、pgfault 是累计——同一套思维)。差异只在工具:XNU 侧靠自研 probe(本会话的 wireperf/cpudelta/hotcold 全是这个传统的延续),Linux 侧现成工具多到选择困难。

附录A源码锚点总表

系统锚点含义
XNUpmap.c:10419 / :10645 / :1068 / :10802 / :10704 / :10768-10780 / :10840REFFAULT 状态机全链(arm_force_fast_fault / SET_REFFAULT_PAGE / PP_ATTR_REFFAULT / WRITE_PTE_STRONG 唯一置 AF 处 / arm_clear_fast_fault / 收割 REFERENCED / arm_fast_fault_internal)
XNUproc_reg.h:1492-1494FSC_ACCESS_FLAG_FAULT_L1/L2/L3 = 0x09/0x0A/0x0B(ARM64 ISA 强制故障)
XNUvm_pageout.c:2950 / :2786-2788 / :2806-2810 / :227老化 1:1 预算 / 50% 自限 / NOFLUSH 注释 / inactive 目标 1/2
XNUvm_resident.c:3173 / :3351 / :360-361vm_page_wait / free_min 唤醒 / 本地队列 250/500
XNUvm_compressor_algorithms.c:296metacompressor(WKdm→LZ4 混合,lz4_threshold=2048)
XNUzalloc.c:476-493 + zalloc_internal.h:294-314四安全选项默认全开(SEQUESTER/SUBMAP/KEXT/STRICT_IOKIT)
XNUkern_memorystatus.c:412/:413/:418/:1310-1326aging 10s/30s / extended 1800MB / 双限额写 ledger
XNUvm_purgeable(:821-1000)purgeable 丢弃顺序遍历
XNUhost.c:815 / :829-831 / :860-861free ⊇ speculative / wire_count 平台分支 / fb-anon pageable 口径
Linuxrmap.c:850 / :767 / :788page_referenced / page_referenced_one / ptep_clear_flush_young_notify
Linuxvmscan.c:1017 / :2742-2766 / :3149 / :3262 / :1939referenced_ptes>1 升级 / 水位 / 直接回收节流 / 直接回收 / 批次 32
Linuxvmscan.c:4589 / :1876(v6.1)+ mmzone.h:384-385lru_gen_look_around 正扫 / 驱逐仍走 try_to_unmap / MIN_LRU_BATCH=64
Linuxarm64 pgtable.h:103/:222 + fault.c:759-761pte_young=PTE_AF / pte_mkold / access flag fault 接入
Linuxkernel/freezer.c:62-90__refrigerator = schedule() 循环(只停 CPU 不搬内存
Linuxmm/madvise.c:743+MADV_FREE 仅匿名 VMA(懒释放提示)
Linuxdrivers/block/zram/zram_drv.c:210 / :1356page_same_filled 同页去重
Linuxmm/workingset.c:40-132refault distance 设计注释(回收质量反馈)

附录B诚实声明与证据分级

本页是 ios-memory-book(v2.50)第 1-17 章的对照阅读伴侣:书负责把 XNU 讲透,本页负责把每一章放进 Linux 镜像前重估一遍。与 ios-memory-book / ios-bigmem-test 同系列。生成于 2026-08-30。