设计原则:每物理页恰记一次。页状态是骨架(天然去重);fp 是进程配额账本(含压缩原始体积);两者交叠用卫生行显式去重,绝不隐藏。
| 行 | 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 里的内存去哪了" | |
| ·wire | 751.7 | vm_stat wired pages ×16K | 物理锁页(不可换出/回收):内核代码/数据、页表、驱动 DMA 缓冲等 |
| ·内核可分页 | 12.9 | N − Σ任务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 单调上升) |
| ·内核压缩stored | 1,033.9 | comp_stored − Σ任务压缩(实测减法) | 压缩器里内核侧住户的原始数据体积(stored 口径 = 压缩前 16K/页计,物理只占槽位);fp 只装了整机 stored 的 63%(任务侧),缺此行则压缩节省行"减了从未加过的量"——合计掉到 5,145.2、差 998.8(§七反例) |
| 预留 | 578.7 | R_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.6 | vm_stat free(⊇ speculative 274.0) | spec 在 free 里已计 |
| 文件缓存 | 1,883.4 | E − 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.0 | wire + 可分页 + 内核压缩stored − iokit残差(IOSurface 锁页改归任务 fp 桶) |
| 预留 | 578.7 | R_chip + R_pool |
| 可用内存(广义) | 2,534.0 | free 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 基线 → S1 拉起 B站/微信/抖音/淘宝/小红书 → S2 相机前台(预览管线全开)→ S3 相机后台化+Safari/Maps/照片/音乐 → S4 全家桶+相机再前台(free 仅剩 4,768 页)。按 §三汇总视图逐场景计算:
| 行(MiB) | S0 基线 | S1 多应用 | S2 相机 | S3 相机退 | S4 极限 |
|---|---|---|---|---|---|
| 生态应用 Σfp | 764.2 | 1,728.0 | 1,739.7 | 1,921.7 | 1,929.9 |
| 系统服务 Σfp | 1,935.0 | 2,024.2 | 2,718.3 | 2,360.3 | 2,874.4 |
| 内核及驱动(净) | 1,762.4 | 1,922.9 | 2,442.6 | 2,030.3 | 2,547.8 |
| 预留 | 586.5 | 566.3 | 560.1 | 564.2 | 555.3 |
| 可用内存(广义) | 2,533.0 | 1,486.1 | 819.8 | 1,265.7 | 782.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.0 | 6,144.0 | 6,144.0 | 6,144.0 | 6,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。
① 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,天然无重复 |
| 合计 Δwire | 985.9 | S9 实测 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)。
所有桶必须落到页状态上才闭合——这就是"每页恰一状态"的可验证形式。常见疑问:R_pool 会不会把 R_chip 重复算?不会——R_pool 的减法(hw.memsize/16384 − Σ页状态)整个发生在页池域内,而 R_chip 在页池域之外(hw.memsize 本身已不含它),两者定义域不相交。真正会重复的是错误算法 dram-size/16384 − Σ页状态(一口吞掉两层边界=37,038 页)再另加 R_chip——本公式先剥第一层(R_chip)、再算池内第二层残差(R_pool),①②两条恒等式同时精确成立就是无重复的证明。
| # | 规则 | 原因 |
|---|---|---|
| 1 | 主干求和只用页状态 | 内核给每页恰一个状态,天然去重 |
| 2 | 压缩换算用节省行整机一条;stored 基数双侧齐全(任务侧在 fp 内、内核侧走内核行) | stored 是数据体积(≈2.0×),物理只占槽;基数不齐 = 减了从未加过的量(差 998.8 实测) |
| 3 | iokit/tag 全归内核桶(唯一归属);NV 不加在 internal 之上 | fp 残差与 wire 重叠;NV 已含在 int/compressed(13.9 实测) |
| 4 | 文件缓存物理单计 E−S;永不用 Σ任务 external / resident_size | spec 已在 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_occ | SwapUsed逻辑 − ZramUsed(换出到 flash 的部分不占 DDR) |
| 预留 R_chip+R_pool | CmaUsed + RsvTotalUsed + HhpFree |
| 文件缓存 E−S | Cached |
| 卫生① iokit×wire 重复 | ION×IOFast 双账本重复(相机态 ~1.76G + 池缓存转化×文件回填 ~1.46G,空闲态 0) |
鸿蒙 16 GB 机同样闭合(空闲 3 快照 ±24 MiB = 0.15%);相机场景同构签名:App 自身 +40 MiB、驱动池 +1,689 MiB。详见主报告 §十一。
proc_listallpids() 只回 73/299 子集 → 用 sysctl KERN_PROC_ALL;② task_for_pid(0) 恒拒;③ mach_vm_region_recurse 对平台二进制 kr=0x4 → 走 BSD 路径;④ 场景表解析防 tagcmp/tagfp 键名覆盖;⑤ 永不 Σ任务 external。