iOS 相机内存三问三答

iPhone 12 Pro · A14(2性能核+4能效核)· 6GB · iOS 14.8(xnu-7195.141.2)· 越狱真机 root 实测 · 16KB 物理页
证据分级:EXP设备实测 · SRC源码行号 · GEN生态经验判断 | 详细数据报告见 姊妹站
问题一:相机内存在不同场景下如何变化?分配效率和回收效率各有多快?
结论:相机管线的内存需求 ≈1.0GB,与内存环境无关(后台干净 Δwired 987MB,五大 App 压力下 Δwired 996MB,差 <1%)。free 充裕时直接从 free 池抽取(回收机器零参与);free 只剩 48MB 时照样 1.77s 全部就位——分配原语本身极快(wire 峰值 1897–3592MB/s),回收按成本阶梯并行供给,jetsam 全程零触发。

两个场景的变化账本 EXP

场景一:后台干净(free 2.4GB)场景二:五大 App 后台(free 48MB)
相机申请(Δwired)+987MB+996MB
free 池变化−1639MB(管线+框架缓存副作用,直接抽取)仅 −42MB(几乎全部由回收供给)
回收机器动作零参与:压缩/逐出/purgeable/jetsam 计数器一字不动文件页逐出 891MB + 匿名压缩 474MB(毛额)+ purgeable/spec 即摘 ~60MB
启动→就位2.04s1.77s(同量级,无可感拖累)

这 1GB 去了哪里:内核无主 wired ≈375MB(ISP DMA 环形缓冲、驱动固件——任何进程账都看不见)+ mediaserverd iokit_mapped +612MB(IOSurface 管线缓冲,真正的主体)+ Camera 壳进程仅 26MB(UI 壳只是管线的"遥控器")。EXP

分配效率:慢在编排,不在分配 EXP

回收效率:按成本阶梯,页级腾挪够用就绝不升级 EXP

顺位手段实测成本
1speculative / purgeable 即摘~60MB,单 tick 内≈0
2文件 clean 页逐出891MB / 2.3s ≈ 380MB/s零 I/O(纯队列操作)
3匿名页压缩474MB 毛额,峰值 682MB/sCPU 换内存
4freeze(冻结落盘)相机窗口 0 新增I/O(五 App 阶段已后台完成 154MB)
5jetsam(杀进程)0 次页级腾挪够用时轮不到

系统没有为相机杀任何进程,五个 App 全部存活。free 探底 44MB 从未枯竭——供给速率始终跟得上分配速率。

问题二:场景二 free 不足 100MB,wired 内存如何做到快速供给 1GB?不还是要先回收吗?
结论:要回收——但关键不是"要不要回收",而是回收在谁的线程里跑。不是"先回收、再锁页"的串行,而是"锁页照常全速进行,回收在别的核上并行补货"的生产者-消费者模型。锁页线程遇缺货的唯一动作是叫醒回收线程然后自己睡觉;三批回收线程(优先级 91,绑能效核)全速补货。

1. 锁页路径是纯消费者——它从不自己回收 SRC

IOKit wire 路径(vm_object_iopl_request,vm_pageout.c:6036–6067)拿页只有三步:

展开后的 vm_page_wait(vm_resident.c:3813)没有一行代码去做扫描/逐出/压缩。防死锁配套:free 池底部的保留池只留给回收机器自己(TH_OPT_VMPRIV,vm_resident.c:3175)——保证生产者永远能拿页继续干活。

2. 真正干活的是三批生产者线程 SRC

线程干什么特权
vm_pageout_scan文件页逐出(零 I/O)、丢 purgeable、把脏匿名页挂压缩队列BASEPRI_VM=91(用户前台 QoS 最高才 47)——抢占一切用户态
VM_compressor ×2从队列批量取页压缩,32 页一批归还 free 池优先级 91 + 绑定 E 簇(能效核)——实测压缩爆发期间性能核全程空闲

3. 实证:供需同窗口开动 EXP

相机启动第一个 250ms 窗口:wire 爆发 1897MB/s同一窗口,供给(文件页逐出 1324 + 压缩 682)= 2006MB/s 同步开动。free 探底 44MB 后,在 wire 仍在爬坡的最猛阶段不降反升,回到 126MB——供给侧追平并反超了消耗,锁页线程全程没有真正饿到。

4. 为什么供给能跟上:三层缓冲

失败模式是"慢"不是"崩"(本轮均未发生):锁页线程睡觉等页 → 代价是相机启动变慢(不是失败)→ 优先级 91 的生产者全速补货 → 仍不够才 jetsam 杀后台兜底(零触发)。

问题三:iOS 的回收流程与 Linux 有什么区别?
结论:释放一个页的"原语"两边几乎一样便宜(摘链表+unmap+归还,都是纯内存操作);真正的区别在三层——①执行位置(iOS 专用线程 vs Linux 申请者线程亲自回收)、②页面选择策略(iOS 固定成本瀑布 vs Linux 比例成本模型)、③匿名页可回收性(iOS 压缩机内建 vs Linux 必须配 swap)。Linux 复刻吞吐毫无障碍,难的是默认就一体化。

区别一(最重要):回收在谁的线程里跑

iOS (XNU)Linux
执行位置全部在专用内核线程(优先级 91、绑 E 簇):申请者从不做回收苦役直接回收在申请者自己的上下文同步执行(try_to_free_pages,vmscan.c:3233)——同一个线程既当消费者又当生产者,这就是 direct reclaim 停顿的根源 SRC
后台线程2 个高优先级压缩线程 + scan 线程kswapd 每 NUMA 节点 1 个,普通优先级、不绑簇(vmscan.c:4043)

区别二:页面选择策略——确定性瀑布 vs 比例模型

iOS(vps_choose_victim_page,vm_pageout.c:2331)SRC:按成本物理分队列,固定瀑布 cleaned(刚洗净)→ 投机型页(预读从未触碰,LRU 之外弹队列即回收)→ 文件页 → 匿名页,裁决全部偏向文件页:

Linux(get_scan_count,vmscan.c:2237+)SRC:swappiness × refault 成本反比加权的比例模型——追求全局回收代价最小(哪边 refault 少多压哪边)。告急时 iOS 行为完全可预测(文件页先扛),Linux 取决于当时的统计。

区别三:匿名页可回收性 + 算法层小差异

iOSLinux
匿名页压缩机内建,无条件可回收必须配置 swap/zram,否则匿名页完全不回收(vmscan.c:2250)——"先堆五个大 App 再开相机"的局,默认 Linux 连压缩这一级供给都拿不出来
压缩算法WKdm 手写汇编(单核 ~780MB/s)+MZV 单值页 4 字节编码+早退检查点+LZ4 择优zram 默认 lz4(单核速度同量级或更快);同值页检测需线性扫描全页 GEN
各领先的项投机型页快车道(免引用检查)、死文件免二次机会(vnode isinuse)+对象级整体逐出workingset refault 距离:逐错保护更精确(iOS 只有引用位粗判);6.1 MGLRU 加速扫描

Linux 能达到同样效果吗?

追问:把"成本瀑布"放上真机量一量——构造投机页 / clean 页 / 脏页三类文件页,再用 wired 加压,回收顺序和效率是否如源码预测?
结论:满足,顺序与源码瀑布完全一致——投机页瞬时弹出(2140MB/s)→ clean 文件页零 I/O 逐出(均速 104MB/s、峰值 2381MB/s)→ 脏页一次都不洗(全程 pageout 仅 6MB)→ 压缩机接管(峰值 926MB/s)。1.89GB wired 分 100 步 mlock,最长停顿 29ms;供给与需求在同一个 50ms 窗口达峰(t+2.0s:wire 2316 vs 供给 2381MB/s);五 App 全活、零 jetsam、零 swap。EXP

用例设计 EXP

在五 App 后台、文件缓存饱和的低压环境(真 free 37MB、投机池 285MB、文件页 1.78GB)下,用 1.5GB 冷文件构造三类文件页,再 mlock 匿名页梯度加压(64→8MB 共 100 步 / 1.89GB / 41s),vmwatch 以 50ms 粒度抓全计数器(2616 样本,与加压步的毫秒时间戳对齐)。

页类构造方法回收顺序位实测效率成本
投机页F_RDADVISE 免触碰预读 768MB(全部落 speculative 队列)第 1 位,前 2.2s 清空瞬时弹出 2140MB/s≈0(物理上就在 free 队列上)
clean 文件页mmap 只读全量 touch 384MB第 2 位,t+2~13s 逐出 940MB均速 104MB/s,峰值 2381MB/s零 I/O(干净页直接丢,数据仍在文件)
脏文件页MAP_SHARED 每页写 1 字节 256MB第 4 位(被跳过)压力窗口内 0 次洗出需 I/O——scanner 拒付,改压缩匿名页(CPU 换内存 2.03×)
匿名页压缩(对照供给源)第 3 位,t+13s 起接管净逻辑 1182MB / 物理 581MB,峰值 926MB/sCPU(八万多页压缩,压缩比 2.03×)

三个关键观测:① 真 free 全程只探底一次——t+3.6s 瞬时 1.5MB(最快 64MB 突发打穿 free 池),50ms 内同窗供给回填到 44MB,此后生产者始终跑赢消费者;② 廉价文件页耗尽即切换——t+13s 后文件逐出降至涓流,压缩机接管,文件缓存稳定 564MB = 脏页 256(不洗)+ 工作集 ~308MB,接近 filecache_min 地板预测 294MB GEN;③ 脏页的账后清——压力窗口零洗出,数据在进程退出路径才落盘(磁盘上 0xAB 模式完整),印证"贵的回收异步化"设计。

供给瀑布时间线
三问三答全部基于:iPhone 12 Pro 真机双场景实测(camwatch/cpuper 自制探针,250ms 粒度全计数器)+ xnu-7195.141.2 与 linux-5.10/6.1 源码逐行核对。
追问实验:filereclaim-exp(vmwatch 50ms 采样 / filefill 三类构造 / wirepress 梯度加压,2026-09-03)。
完整数据、图表与实验方法学见 详细报告站(姊妹篇)。更新于 2026-09-03。