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.5三部分(下三行)——回答"内核/驱动不记在任何 fp 里的内存去哪了"
·wire751.7vm_stat wired pages ×16K物理锁页(不可换出/回收):内核代码/数据、页表、驱动 DMA 缓冲等
·内核可分页12.9N − Σ任务internal − Σ任务volatile驻留(减法)。N = vm_statistics64.internal_page_count("anonymous 页数",host_statistics64(HOST_VM_INFO64) 直读,host 级统计无需特权;恒等式 N+ext = active+inactive+speculative 实测精确成立)内核/驱动自己的可回收匿名页,物理活在 N 状态(压力下可回收/可压缩);task_for_pid(0) 恒拒 → 无正向账本,只能减法;驻留时计本行,被压缩后改计入下行(场景联动:S0→S4 本行 4.2→62.8 波动、下行 1,034.5→1,308.4 单调上升)
·内核压缩stored1,033.9comp_stored − Σ任务压缩(实测减法)压缩器里内核侧住户的原始数据体积(stored 口径 = 压缩前 16K/页计,物理只占槽位);fp 只装了整机 stored 的 63%(任务侧),缺此行则压缩节省行"减了从未加过的量"——合计掉到 5,145.2、差 998.8(§七反例)
预留578.7R_chip 408.05(dram-size − hw.memsize)+ R_pool 170.7(hw.memsize/16384 − Σ页状态)R_chip = OS 之外的固件预切(SEP/AOP);R_pool = OS 页池之内、任何页状态都数不到的页:secluded 9,600 页=150.0(ANE 隔离区,ioreg kern.secluded_mem_mb)+ 残差 1,323 页=20.7(未逐项定位)。两层边界定义域不相交:R_pool 的减法整个发生在页池域内,而 hw.memsize 本身已不含 R_chip → 相加无重复(会重复的错误算法是 dram-size/16384−Σ页状态 一口吞两层再另加 R_chip);恒等式①②同时精确闭合即无重复的证明
可用(立即)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_nonvolatile(场景 S0–S4:17.5/64.6/82.7/111.1/87.4)purgeable 非易失内存——App 用可清除 API(mach_vm_purgable_control / NSPurgeableData)标记的"持有勿丢"页(purgeable 两态:volatile=允许系统压力下直接丢弃、fp 不含;nonvolatile=持有声明、fp 计入)。被统计到 fp 里了——而且被统计了两次:fp 恒等式把它作独立项 [SRC task.c:991-996],但这些页本质上还是任务匿名页,internal/compressed 账本同时也收了它们(内核故意的问责加成)。数值自证:kp 12.9 < ΣNV 13.9——若 NV 不在 Σint 内会被 kp=N−Σint−Σvlt 吸收(kp 必 ≥13.9),实测否 → NV 必在 Σint 内。不扣③则整机合计恰好多出 ΣNV
fp卫生 ④ −tagfp−10.7Σ tagged 账本(media/graphics/network/neural footprint)tagged footprint 与内核侧交叠 → 扣
合计6,144.0= 6 GiB ✓(五场景 S0–S4 全部偏差 0.0)

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

行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 五个极端状态 · 全部 6,144.0

S0 基线 → S1 拉起 B站/微信/抖音/淘宝/小红书 → S2 相机前台(预览管线全开)→ S3 相机后台化+Safari/Maps/照片/音乐 → S4 全家桶+相机再前台(free 仅剩 4,768 页)。按 §三汇总视图逐场景计算:

行(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
内核及驱动(净)1,762.41,922.92,442.62,030.32,547.8
预留586.5566.3560.1564.2555.3
可用内存(广义)2,533.01,486.1819.81,265.7782.0
压缩节省行−1,407.6−1,492.4−2,033.9−1,817.8−2,442.4
fp 校准项−29.4−91.0−102.6−180.4−103.0
合计6,144.06,144.06,144.06,144.06,144.0

汇总口径换算(明细子行就地并入三个汇总行,与明细视图同项重排恒等):内核净 = wire+内核可分页+内核压缩stored − iokit残差①(如 S2:1,778.2+81.4+1,252.4−669.4 = 2,442.6);广义可用 = free+文件缓存+volatile驻留②(如 S1:214.5+1,255.7+15.9 = 1,486.1;S0 为 626.0+1,886.8+20.1 ≈ 2,533.0,页级未舍入口径);fp校准 = −NV③−tagfp④(如 S3:−111.1−69.3 = −180.4)。广义可用 2,533.0→782.0 MiB(41.2%→12.7%):相机+全家桶把"实际可用"吃到只剩 1/3(文件缓存被主要征用 1,886.8→698.0);内核净桶相机场景涨到 2,442.6——相机锁页缓冲未映射进任何任务 fp 的部分留在内核,已映射部分(608.5~669.4)归系统服务 fp 桶。底层:free 40,067→4,768 页、wire 47,731→114,242 页、压缩槽 90,070→144,920 页,五个极端状态合计恒 6,144.0 偏差 0.0。

五、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)。

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

① Σ页状态 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 + ... = Σ页状态)

所有桶必须落到页状态上才闭合——这就是"每页恰一状态"的可验证形式。常见疑问:R_pool 会不会把 R_chip 重复算?不会——R_pool 的减法(hw.memsize/16384 − Σ页状态)整个发生在页池域内,而 R_chip 在页池域之外(hw.memsize 本身已不含它),两者定义域不相交。真正会重复的是错误算法 dram-size/16384 − Σ页状态(一口吞掉两层边界=37,038 页)再另加 R_chip——本公式先剥第一层(R_chip)、再算池内第二层残差(R_pool),①②两条恒等式同时精确成立就是无重复的证明。

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

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 口径求和的卫生规则

#规则原因
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。