什么叫“脏页”?
脏页(dirty page)= 内容已被进程修改过的物理页。
当一块虚拟页第一次被载入 RAM 时,它要么来自某个文件(Mach‑O 代码、PNG 资源等),要么是匿名零页(堆、栈、BSS)。此时它是 干净页 (clean)——磁盘里已有一份等价拷贝或可以重新生成。CPU 对该页发生任何写操作 时,MMU 会把页表里的 dirty 位设为 1,表示「RAM 版本已不同于后备存储」。这就是“脏”。citeturn4view0
两种最常见的脏页来源
区域 | 变脏的典型场景 | 结果 |
---|---|---|
匿名页(堆 / 栈 / malloc 区) | 首次写入对象、数组、临时变量 | 页内数据只存在于 RAM;除非压缩或杀进程,否则系统无法回收 |
可写映射文件(Core Data WAL、缓存数据库等) | 进程经 mmap(PROT_WRITE) 更新文件 | 页面最终会在 msync() 或文件关闭时写回闪存 |
只读代码、图片、字符串常量等 file‑backed + 只读 段几乎永远保持 clean;它们在内存压力下可直接丢弃,下次缺页时再从闪存重读。
为什么要区分“脏/干净”?
内核决策 | Clean 页 | Dirty 页 |
---|---|---|
内存回收 | 直接标记未驻留即可 | iOS 没有 swap: • 若是匿名页→只能尝试压缩; • 压缩池也满时→进入 Jetsam 淘汰进程 |
应用被挂起 | 可整块清零,后台挂起体积很小 | 必须留在 RAM;Dirty 太多 → 挂起时就被 Jetsam 杀掉 |
VMTracker/Instruments 统计 | 计入 Clean 或 Reclaimable | 计入 Dirty;这部分才是真正无法随时释放的 “压力” |
在 iOS 的 VM 模型里始终成立:
Resident ≥ Dirty
因为 Dirty 页一定常驻 RAM,而 Clean 页随时可被回收。随着你不断写入数据,Dirty 增长;当收到 Memory Warning 却仍不释放,系统压缩失败后就会 Jetsam 你的进程。citeturn4view0
开发者如何降低 Dirty 内存
- 及时释放:图片解码后不用就
nil
掉;大数组处理完立刻 free/reset。 - 用映射文件而非堆:大而少改的数据(例如离线地图瓦片索引)用
mmap(PROT_READ)
,让它保持 clean。 - 利用
NSData
/IOSurface
的 purgeable 属性:可声明为“可丢弃”,核心映射在压力大时变为 clean,被系统自动蒸发。 - 避免无谓写入:频繁把 JSON 解析为可变字典、在热路径上修改字符串都会制造额外 Dirty。
简单记住——写=脏,脏=难回收。在 Instruments 里盯住 Dirty Size,它才是真正决定你的 App 会不会被内存警告、会不会被 Jetsam 的硬指标。