iOS 整机内存拆解公式 · 校准版

iPhone 12 Pro · iOS 14.8.1 (xnu-7195.141.2) · 6 GiB DDR · 五场景实测闭合 6,144.0 偏差 0.0 · 2026-11 校准

一、一句话公式两个视图 · 同项重排恒等

【明细视图】 总DDR 6,144.0 = 生态Σfp(APP) + 系统Σfp(SYS) + 内核(wire+可分页+内核压缩stored) + 预留(R_chip+R_pool) + free + 文件缓存(E−S) − (comp_stored − comp_occ) ← 压缩节省行(整机一条) + fp卫生( ①−iokit残差 ②+volatile驻留 ③−NV账本 ④−tagfp ) 【汇总视图】 总DDR 6,144.0 = 生态Σfp + 系统Σfp + 内核净(内核−iokit残差) + 预留 + 可用(广义)(free+文件缓存+volatile驻留) − (comp_stored − comp_occ) − fp校准项(NV+tagfp)

设计原则:每物理页恰记一次。页状态是骨架(天然去重);fp 是进程配额账本(含压缩原始体积);两者交叠用卫生行显式去重,绝不隐藏。

二、逐行说明(run4 实测值)

行MiB数据源(怎么算)为什么这么算
生态应用 Σfp(APP)773.6每个 App 进程 task_vm_info.phys_footprint 求和fp 是配额视图:internal + compressed(stored 原始体积) + iokit_mapped + NV + page_table
系统服务 Σfp(SYS)1,936.3系统进程同口径同上
内核及驱动1,798.5wire 751.7(vm_stat)+ 内核可分页 12.9(N−Σint−Σvlt 减法)+ 内核压缩stored 1,033.9(comp_stored−Σ任务压缩)锁页/可分页按页状态;内核侧压缩数据必须补基数(见 §4)
预留578.7R_chip 408.05(ioreg dram-size − hw.memsize)+ R_pool 170.7(secluded 150 + 残差 20.7)固件预切与 ANE 隔离区对 OS 不可见
可用(立即)631.6vm_stat free(⊇ speculative 274.0)spec 在 free 里已计
文件缓存1,883.4E − S(external − speculative)文件页物理单计;永远不用 Σ任务 external / resident(缺页记账 2.1×)
压缩节省行−1,423.0−(comp_stored − comp_occ),整机一条fp 基数装的是 stored 数据体积(≈2.0×),物理只占槽位 → 差额就是压缩器替整机省下的 DDR
fp卫生 ① −iokit残差−29.5Σfp − Σint − Σ任务stored − ΣNV − Σtagfp(账本恒等式移项)IOSurface/GPU 缓冲记在进程 fp 又占 wire(双计)→ 从 fp 侧扣、归内核桶唯一归属
fp卫生 ② +volatile驻留+19.0Σ任务 purgeable volatile 驻留volatile 驻留 fp 不含(对系统"白送"),但物理在 N 桶 → 补回
fp卫生 ③ −NV账本−13.9Σ ledger_purgeable_nonvolatileNV 与 internal/compressed 账本级重复(实测加上恰好多 13.9)
fp卫生 ④ −tagfp−10.7Σ tagged 账本(media/graphics/network/neural footprint)tagged footprint 与内核侧交叠 → 扣
合计6,144.0= 6 GiB ✓(五场景 S0–S4 全部偏差 0.0)

三、骨架:三条底层恒等式公式成立的依据

① Σ页状态 356,178 + R_pool 10,923 = 367,101 = hw.memsize/16384 ✓ ② R_chip 26,115 + 367,101 = 393,216 = 6 GiB 整页 ✓ ③ ext + int = active + inactive + speculative ✓ (wired + purgeable + comp_occ + 内核可分页 + free + speculative + ... = Σ页状态)

所有桶必须落到页状态上才闭合——这就是"每页恰一状态"的可验证形式。

四、压缩节省行的算术最容易错的一行

stored 基数(双侧必须齐全): 任务侧 1,794.9(在 fp 内)+ 内核侧 1,033.9(内核行)= comp_stored 2,828.8 ✓ 节省行 = −(comp_stored 2,828.8 − comp_occ 1,405.8) = −1,423.0 ✓
⚠ 反例(实测):若内核桶不补「内核压缩stored 1,033.9」这一行,合计 = 5,145.2、差 998.8 MiB——节省行把内核侧的压缩节省也减了,但内核 stored 从未加进任何桶(fp 只装了整机 stored 的 63%),且内核压缩槽位(513.8)无桶收留。内核侧 stored 是实测减法(comp_stored − Σ任务压缩),不是推算。

五、fp 卫生行详解为什么这四行存在

① iokit 残差 = fp 与 wire 的重复记账(最大的一条)。内核账本机制 [SRC xnu-7195 task.c:986-1009 / vm_map.c:20208-20229]:IOKit 映射进任务时内核同时打点两列——ledger_credit(iokit_mapped, bytes); ledger_credit(phys_footprint, bytes);(注释原话 "bump both counters")。这些 DMA 缓冲物理上是锁页 → 同一批页,fp 按映射配额收一次,wire 按物理状态收一次。14.8 的 task_vm_info 不暴露 iokit_mapped/alternate/page_table 列(新版 xnu 才暴露)→ 残差 = fp − 已暴露分项 = 未暴露列的精确打包和。

相机场景的直接证据(S5↔S6 / S9 A/B):mediaserverd 的 IOSURFACE 映射 510.2↔14.0 MiB(442→2 个区域,VM map 按 user_tag 直读),与 fp 残差 474.5↔4.5 同步摆动(吻合 92%);wire 1,781.4↔795.5。

Δwire(相机开↔关)MiB在 fp 里吗记账处理
mediaserverd 映射的 IOSurface 像素缓冲496.3是(fp iokit_mapped 按配额收)重复 → 去重一次
驱动/内核私有(ISP DMA 环、驱动缓冲)489.6否(任何 fp 都不含)只在 wire,天然无重复
合计 Δwire985.9S9 实测 1,781.4 ↔ 795.5

"全独立页"假设三重反驳:① 状态穷尽(映射页只能住 wire 或 N)② N 无空间(内核可分页相机开时 133.8→81.4 不升反降)③ 幅度对账(全独立需 wire 涨 ~1,482,实测 985.9)。误判为"全额外"则合计 +496.3 爆表。

②③④ 是账本级小重复:volatile 驻留是 fp 故意不含的"白送"页(物理在 N,需补回);NV 账本与 internal/compressed 重复(实测 13.9);tagged 账本与内核侧交叠(10.7)。

六、汇总视图语义用户指定口径

行MiB构成
生态应用 / 系统服务773.6 / 1,936.3Σfp 不变
内核及驱动(净)1,769.0wire + 可分页 + 内核压缩stored − iokit残差(IOSurface 锁页改归任务 fp 桶)
预留578.7R_chip + R_pool
可用内存(广义)2,534.0free 631.6(立即 10.3%)+ 文件缓存 1,883.4(可回收 30.7%)+ volatile 19.0(可丢弃 0.3%) = 全机 41.2%
压缩节省行−1,423.0−(comp_stored − comp_occ)
fp 校准项−24.6−NV 13.9 − tagfp 10.7(合并)
合计6,144.0与明细视图恒等 ✓

七、跨场景校准矩阵S0–S4 五个极端状态

行 (MiB)S0 空闲S1 压后台S2 相机开S3 相机关S4 深压
生态应用 Σfp764.21,728.01,739.71,921.71,929.9
系统服务 Σfp1,935.02,024.22,718.32,360.32,874.4
·wire745.8794.41,778.2819.61,785.0
·内核可分页4.2133.881.4245.062.8
内核压缩stored1,034.51,059.51,252.41,087.81,308.4
预留586.5566.3560.1564.2555.3
free / 文件缓存626.0 / 1,886.8214.5 / 1,255.799.0 / 720.7317.3 / 921.574.5 / 698.0
压缩节省行−1,407.6−1,492.4−2,033.9−1,817.8−2,442.4
卫生① iokit残差22.064.8669.4122.1608.5
卫生② volatile20.115.90.127.09.6
卫生③ NV17.564.682.7111.187.4
卫生④ tagfp11.926.419.969.315.6
广义可用2,533.01,486.1819.81,265.7782.0
合计6,144.06,144.06,144.06,144.06,144.0

free 40,067→4,768 页、wire 47,731→114,242 页、压缩槽 90,070→144,920 页——五个极端状态合计恒 6,144.0 偏差 0.0。相机签名:mediaserverd fp 38.4→708.4→48.6 MiB 锁步 wire +984/−958;相机 App 自身 fp 全程仅 ~24 MiB(成本住在 mediaserverd fp + wire,不在 App)。

八、去重四规则fp 口径求和的卫生规则

#规则原因
1主干求和只用页状态内核给每页恰一个状态,天然去重
2压缩换算用节省行整机一条;stored 基数双侧齐全(任务侧在 fp 内、内核侧走内核行)stored 是数据体积(≈2.0×),物理只占槽;基数不齐 = 减了从未加过的量(差 998.8 实测)
3iokit/tag 全归内核桶(唯一归属);NV 不加在 internal 之上fp 残差与 wire 重叠;NV 已含在 int/compressed(13.9 实测)
4文件缓存物理单计 E−S;永不用 Σ任务 external / resident_sizespec 已在 free;external/resident 是缺页记账(2.1×)

九、跨内核对照:鸿蒙同构公式结构移植验证

iOS 维度鸿蒙对应(HarmonyOS 7 / HongMeng 1.13)
生态/系统ΣfpΣ(Pss_Anon + Pss_Shmem) 按进程分类
内核(wire+可分页+压缩stored)SUnreclaim+SReclaimable+内核栈/动态/页表/vmalloc/DireAllocate+ZramUsed
节省行 comp_stored−comp_occSwapUsed逻辑 − ZramUsed(换出到 flash 的部分不占 DDR)
预留 R_chip+R_poolCmaUsed + RsvTotalUsed + HhpFree
文件缓存 E−SCached
卫生① iokit×wire 重复ION×IOFast 双账本重复(相机态 ~1.76G + 池缓存转化×文件回填 ~1.46G,空闲态 0)

鸿蒙 16 GB 机同样闭合(空闲 3 快照 ±24 MiB = 0.15%);相机场景同构签名:App 自身 +40 MiB、驱动池 +1,689 MiB。详见主报告 §十一。

十、复现要点

工具: ios-probe/cli/memcensus.c (全机普查: M/V/E/K/S + 每进程 fp/int/ext/cmp/nv/vlt/tag 账本) ios-probe/cli/iokitmap.c (VM map user_tag 直读: mach_vm_region_recurse/App + proc_pidinfo(PROC_PIDREGIONINFO)/平台二进制) 闭合核验: states+R_pool==hw.memsize/16384 ; R_chip+memsize==dram-size ext+int==active+inactive+speculative ; stored 双侧==comp_stored
工具坑(实测):① proc_listallpids() 只回 73/299 子集 → 用 sysctl KERN_PROC_ALL;② task_for_pid(0) 恒拒;③ mach_vm_region_recurse 对平台二进制 kr=0x4 → 走 BSD 路径;④ 场景表解析防 tagcmp/tagfp 键名覆盖;⑤ 永不 Σ任务 external。