iPhone 12 Pro · 6G DDR 五维拆解与正交恒等式
实测设备:iPhone 12 Pro · A14 · iOS 14.8.1 (18H17, xnu-7195.140.44) · 越狱(Taurine) · 解锁态 · 页大小 16KB
采样:2026-10-31 · memcensus v3 原子快照(单次 host_statistics64)+ 293/295 进程全量普查(零拒绝)
口径:每项均给出源(sysctl / vm_statistics64 / task_vm_info / ioreg),可复现
一、核心结论先看这个
DDR(6 GiB) = R_chip + W + F + (E + N − S) + C + T + R_pool
← 页状态分桶,天然正交,页页闭合
能正交相加,但必须换分母:用「页状态」做桶 → 各项互斥、严格可加(下面每项都实测闭合到页);
用「进程 footprint」做桶 → 必炸:朴素五维求和 = 5198.7 MiB ≠ 6144 MiB,差 945.3 MiB,五个交叉项已逐一量化(见第四节)。
修正版五维(文件缓存计入可用 + 去两处重复)已做到严格加总 = 6,144.0 —— 见第七节。
⚠ 最容易踩的第一个坑:hw.memsize = 5735.95 MiB,不是 6 GiB。
物理芯片 6 GiB 要从 ioreg dram-size 读(0x180000000 = 6,442,450,944 B)。第一层 408.05 MiB 固件预切,OS 根本看不见。
二、三层 DDR 恒等式全部实测闭合
408
752
E+N−S 2776
C 1406
F 632
171
R_chip 固件预切
W wired(内核及驱动主体)
E+N−S 文件缓存+用户态匿名(扣 speculative)
C 压缩器槽位
F 可用(⊇speculative)
R_pool 页池内预留
| 层 / 状态 | 页数 | MiB | 含义与数据源 |
| 物理 DDR | 393,216 | 6,144.0 | ioreg dram-size = 0x180000000 |
| ├ R_chip(固件预切) | 26,115 | 408.05 | dram-size − hw.memsize(SEP/AOP 等,OS 不可见) |
| └ hw.memsize | 367,101 | 5,735.95 | sysctl hw.memsize(= host_basic.max_mem) |
| ├ W wire | 48,111 | 751.7 | vm_stat「Pages wired down」=内核及驱动主体 |
| ├ F free(立即可用) | 40,425 | 631.6 | 含 speculative 17,536 页(274.0 MiB) |
| ├ active | 92,470 | 1,444.8 | E=ext 138,073(2,157.4)+ N=int 57,137(892.8)= active+inactive+speculative(恒等式③) |
| ├ inactive | 85,204 | 1,331.3 |
| ├ C 压缩器占用 | 89,968 | 1,405.8 | 槽位;内存放 181,044 stored 页(2,828.8 MiB)数据 |
| ├ T throttled | 0 | 0.0 | vm_stat「Pages throttled」 |
| └ R_pool(页池内预留) | 10,923 | 170.7 | secluded 9,600 页=150 MiB(ioreg kern.secluded_mem_mb,ANE 隔离区)+ 残差 1,323 页 20.7 MiB |
恒等式验证(5 个快照全部精确成立):
① Σ页状态 356,178 + R_pool 10,923 = 367,101 = hw.memsize/16384 ✓
② R_chip 26,115 + 367,101 = 393,216 = 6 GiB 整页 ✓
③ E + N = 138,073+57,137 = 195,210 = active+inactive+speculative ✓ (连续 5 快照)
④ W + F + (E+N−S) + C + T + Rp = 48,111+40,425+177,674+89,968+0+10,923
= 367,101 页 = hw.memsize ✓
⑤ Σ任务 volatile 账本 1,216 页 ≈ vm_stat「Pages purgeable」1,270 ✓
S(speculative 17,536 页)在公式中必须显式扣一次:它物理上在 free 里、归属上又在 E+N 里——双重身份,只许出现一次。
三、五维归属实测293/295 进程全量普查
生态 774
系统服务 1936
内核≈1278
预留 579
可用 632
缺口 945
| 维度 | 实测 | 进程数 | 口径与构成 |
| 生态应用 | 773.6 MiB | 16 | Σ phys_footprint(路径 /var/containers/):bili 277.8 · News 256.1 · WeChat 91.2 · imeituan 64.6… |
| 系统服务 | 1,936.3 MiB | 277 | Σ phys_footprint(其余全部):SpringBoard 93.6 · backboardd 89.9 · launchd 82.5 · WebKit×4… |
| 内核及驱动 | ≈1,278 MiB | — | wire 751.7(实测)+ 内核可分页 12.9(实测 N 未归属)+ 压缩器未归属槽位 513.8(按 stored 占比推算) |
| 预留 | 578.7 MiB | — | R_chip 408.05 + R_pool 170.7(secluded 150 + 残差 20.7) |
| 可用 | 631.6 MiB | — | free 立即可用(⊇ speculative 274.0);另有文件缓存 2,157.4 属「潜在可回收」未计入 |
| 朴素五维求和 | 5,198.7 | ≠ 6,144 → 差 945.3 MiB,见下一节 |
归属陷阱①:WebKit 渲染进程(为 bili/News 渲染网页)住在 /System/Library/Frameworks/WebKit.framework/,
被分进「系统服务」桶——生态应用的真实成本被低估。陷阱②:volatile purgeable 驻留页记在 owner 的
volatile 账本(实测 Σ 1,216 页 ≈ vm_stat 1,270 ✓),不在 internal 字段。
四、交叉项量化五维不能朴素相加的四个原因
| # | 交叉项 | 量化实测 | 后果 |
| ① | 压缩记账换算差 | footprint 按数据 stored 体积记 1,743.7 MiB,物理只占槽位 1,405.8 MiB | 五维 footprint 口径高估 ~338 MiB |
| ② | 共享页双记账(external / resident 字段) | Σ任务 external = 290,724 页 ≈ 2.1× 物理 138,073 页;Σ resident = 5,457 MiB > 6 GiB | 污染 external/resident 字段——所幸 external 不进 footprint;fp 真正的交叠只有 iokit 残差 29.4 MiB(与内核 wired 桶重叠) |
| ③ | 文件缓存盲区 | 2,157.4 MiB 不在任何 footprint 里,但物理在 DDR 里 | 五维覆盖不了这部分 |
| ④ | speculative 双重身份 | 274.0 MiB 既在 free 又在 E+N | 公式里必须 −S 只算一次 |
| ⑤ | NV 账本陷阱 | ledger_purgeable_nonvolatile 13.9 MiB 若加在 internal 之上恰好多出 13.9 | NV 已含在 int/compressed 里——实测 Σint+Σvolatile+内核可分页 = N 精确闭合,无需 NV 项 |
差额精确对账(run4 实测):缺口 945.3 = 文件缓存未计入 +1,883.4 − 压缩换算差 902.9 − iokit/tag 交叠 29.4(余 5.7 为槽位推算的舍入)。加文件缓存是必要条件,但不充分——见第六节演示。
五、优化正交公式推荐口径
DDR(6 GiB) = R_chip ← ioreg dram-size − hw.memsize
+ W wire ← vm_stat wired (内核及驱动主体)
+ F free ← vm_stat free (可用,⊇ speculative)
+ (E + N − S) ← E=ext, N=int, S=speculative(E+N=active+inactive+S 恒等式③)
+ C compressor ← 「Pages occupied by compressor」
+ T throttled ← 常为 0
+ R_pool ← hw.memsize/16384 − Σ页状态(secluded + 残差)
五维映射(归属视图,叠加在 N、C 桶内部做分子拆分):
生态应用 = Σ fp(APP) · 系统服务 = Σ fp(SYS)
内核及驱动 = W + N_未归属 + C_未归属份额(按 stored 占比推)
预留 = R_chip + R_pool · 可用 = F
⚠ 归属视图参与求和必须显式带 Δ交叉项(压缩换算、iokit 交叠、文件缓存、−S),
否则差 945.3 MiB;正交求和请只用页状态公式。
六、把文件缓存加进「可用」就行了吗?对账演示:会超 938 MiB
朴素五维 5,198.7
+ 文件缓存 E−S 1,883.4 ← 五维确实漏了它,该加
─────────────────────────
= 7,082.1 MiB vs 6,144 → 超出 938.1(不是补齐!)
超出的 938.1 恰好就是两处重复记账:压缩换算差 902.9 + iokit/tag 交叠 29.4(余 5.7 舍入)。
所以正确操作是「加文件缓存 + 去两处重复」同时做——做完即得修正版五维(第七节),严格加总 = 6,144.0。
| 「可用」的两种口径 | 数值 | 说明 |
| 立即可用(free) | 631.6 MiB | 分配器直接拿,零成本(内含 speculative 274.0) |
| 宽口径可用(free + 文件缓存 E−S) | 2,515.0 MiB | 合理口径,但回收有代价:文件页清掉后重用需重新 IO;active 里的文件页要先老化 |
压缩换算差是什么(bili 实例):
fp 记的 compressed = 264.4 MiB(数据"原始体积",stored 口径)
物理占用 ≈ 264.4 ÷ 2.0 = ~131 MiB 槽位(整机压缩率 181,044 stored ÷ 89,968 槽 = 2.0×)
→ 全局 Σ任务 compressed = 1,794.9 MiB「数据」,但压缩机物理只占 1,405.8 MiB
→ fp 桶(stored 口径)与内核槽位桶同表相加 = 同一批 DDR 算两遍(多算 ~902.9)
为什么这样设计:footprint 是配额/杀伤视角("解压回来是多少"),对 jetsam 正确,对物理账错误。
共享双记账是什么(含更正):
external 字段 = 缺页记账:谁触碰过共享文件页(dyld 共享缓存/系统资源)谁账上记一笔
→ Σ任务 external = 290,724 页 = 2.1× 物理的 138,073(物理一页,账本 N 笔)
⚠ 更正:external 不进 phys_footprint,此 2.1× 不污染五桶 fp 求和;
fp 真正的交叠只有 iokit 残差 29.4 MiB(fp − int − cmp − nv − tagfp 实测)。
警示反例:谁用 resident_size 建桶,Σres = 5,457 MiB > 6 GiB,直接穿帮。
七、修正版五维(物理去重)严格加总 = 6,144.0 MiB ✓
| 维度(物理去重) | 页数 | MiB | 构成(实测 run4) |
| 生态应用 | 26,997 | 421.8 | internal 4,763 页 + volatile 79 页 + 压缩槽 22,140 页(fp 视角 773.6) |
| 系统服务 | 86,402 | 1,350.0 | internal 50,319 + volatile 1,137 + 压缩槽 34,946(fp 视角 1,936.3) |
| 内核及驱动 | 81,818 | 1,278.4 | wire 48,111 + 可分页 824 + 压缩槽 32,883(减法定义,保闭合) |
| 预留 | 37,038 | 578.7 | R_chip 26,115 + R_pool 10,923 |
| 可用(立即) | 40,425 | 631.6 | free(⊇ speculative 17,536) |
| 文件缓存(可回收,可并入可用) | 120,537 | 1,883.4 | E − S |
| 合计 | 393,216 | 6,144.0 | = 6 GiB 整,页页闭合 ✓ |
去重四规则若坚持五维直接相加
| # | 规则 | 原因 |
| 1 | 主干求和只用页状态 | 内核给每页恰一个状态,天然去重,什么都不用扣 |
| 2 | 压缩求和用槽位口径;内核侧槽位用减法定义(comp_occ − 任务侧) | stored 是数据体积(2.0×),物理只占槽;减法定义让闭合精确 |
| 3 | iokit/tag 全归内核桶(唯一归属);NV/volatile 账本不加在 internal 之上 | fp 残差与 wire 重叠;NV 已含在 int/compressed(实测教训 13.9 MiB) |
| 4 | 文件缓存物理单计 E−S;永不用 Σ任务 external / resident_size | spec 已在 free;external/resident 是缺页记账(2.1×) |
验证器(每次普查后跑三条):① Σint+Σvolatile+内核可分页 = N(实测 57,137 精确)② 任务槽+内核槽 = comp_occ ③ 修正五维和 = 393,216 页。任务侧压缩槽按 stored 占比分摊是推算,但总和按构造精确。
八、原始数据run4 原子快照 + 普查合计
| host_statistics64 原子快照 |
| free / speculative | 40,425 / 17,536 页 |
| active / inactive | 92,470 / 85,204 页 |
| wired / throttled | 48,111 / 0 页 |
| purgeable(volatile) | 1,270 页 |
| ext / int | 138,073 / 57,137 页 |
| comp_occ / comp_stored | 89,968 / 181,044 页 |
| 进程普查(KERN_PROC_ALL) |
| APP(16)/ SYS(277) | 773.6 / 1,936.3 MiB fp |
| Σtask internal / 全局 int | 55,097 / 57,137 页 |
| 未归属 internal(≈内核可分页) | 824 页(12.9 MiB) |
| Σtask compressed(stored)+tag | 114,874 页 |
| 未归属 stored → 槽位推算 | 32,883 页(513.8 MiB) |
| Σtask volatile 账本 | 1,216 页 |
| TOP 进程(footprint) | MiB | 桶 | TOP 进程(footprint) | MiB | 桶 |
| bili-universal | 277.8 | 生态 | WeChat | 91.2 | 生态 |
| News | 256.1 | 生态 | backboardd | 89.9 | 系统 |
| SpringBoard | 93.6 | 系统 | launchd | 82.5 | 系统 |
九、复现方法与工具坑全部可在越狱设备上重放
# ① 三层数字
sysctl hw.memsize hw.pagesize
vm_stat # 各页状态
ioreg -p IODeviceTree -l | grep -E "dram-size|secluded" # 6GiB 物理与 secluded
# ② 全进程普查(探针 ios-probe/cli/memcensus.c,编译后设备侧 ldid 签名)
xcrun -sdk iphoneos clang -arch arm64 -o memcensus memcensus.c
# 部署后: /var/mobile/ios_memslice/memcensus
# 输出 M/V/E/K/S 行 + 每进程 P 行(fp/internal/external/compressed/账本)
# ③ 恒等式核验(本页所有 ✓)
# states+R_pool==hw.memsize/16384 ; R_chip+memsize==dram-size
# ext+int==active+inactive+speculative
工具坑(实测踩过):① proc_listallpids() 在 iOS 只返回 73/295 子集——必须用
sysctl KERN_PROC_ALL,否则系统服务桶低估 5 倍;② task_for_pid(0) 恒拒
kr=0x5(四项 entitlements 也不行),内核桶只能减法+推算;③ 共享文件页被每任务重复记 external 账本,物理求和勿用 Σtask ext。