业务 × 内存 抓栈拆解

malloc / IOSurface / purgeable 三线闭环 —— 结论、方法论与 Instruments 对比

iPhone 12 Pro · A14 arm64e iOS 14.8 (xnu-7195.141.2) Taurine 越狱 frida-server 17.18.0 采集日期 2026-10-27

01一句话总览

同一条方法论(公开符号 Interceptor hook + 分层采集 + 协议工程 + 内核源码对齐),三条互相印证的线,把"业务申请了多少内存、申请了什么、何时声明可丢弃"全部落到带调用栈的事件级证据上。

malloc(怎么申请·高频小额·churn 主导)→ IOSurface(申请什么·低频大额·持久)→ purgeable(何时声明可丢/取回·状态机迁移)

02内存拆解结论(业务全景)

三线数据合成一张表:IOSurface = 图形/媒体面,malloc 毛申请 = 分配压力,in_use Δ / purgeable = 净持有与可丢纪律。

业务IOSurface 申请malloc 毛申请 / 窗口净变化主导路径(实抓栈)
相机管线
mediaserverd
531 MB 865.3 MB / 50s
338 万次调用
in_use +66.1MB 全系统最大瞬时内存事件:CVPixelBufferPool 515MB + FigPhoto JPEG 70MB + ANE 14MB ‖ H13ISP 逐帧元数据 176.8MB ← JasperDepth 深度引擎 112.7MB ← 点云 38.8MB
快手 34.4 MB 460.9 MB / 60s
690 万次调用
in_use +1.4MB
存活 28.4MB
Metal IOGPUResourceListInit 命令缓冲池 19MB · NSKeyedArchiver 序列化 11MB · JSON 8MB · anon 热补丁代码 36MB
美团 7.4 MB 122.9 MB / 60s
58.6 万次
in_use +4.8MB JSON 解析驱动:NSJSONReader 22.3MB · dispatch_data 网络 8.1MB · gzip 压缩 6.8MB
bilibili 56.3 MB 39.8 MB / 60s
15.9 万次
in_use +10.4MB 自研 AVIF 解码器 13.7MB(BFCImageAVIFCoder)· CG 图像 2.9MB · boringssl TLS 2.2MB
京东 ≈0 3.7 MB / 60s
4.4 万次
in_use −0.8MB Cronet 双向流 · 小额为主,最安静的业务
淘宝 118.6 MB 不可测(慢启动)— IOSurface 面 = MediaToolbox FigPhoto 解码池;malloc 面冷启动 >45s 仍在闪屏
微信 13 MB 不可测(反调试)— frida script 加载层挂死 rpc;低频 purgeable hook 也无解
网易云 / 小红书 27.4 MB 不可测(永续分配流)— 内容流 prefetch 永不停歇:+13/25/45s 任何时点挂钩必触发看门狗
抖音 42 MB 稳态 不可测(反调试)— 稳态 0 新建 + 42MB 持有已知
SpringBoard ~92 MB / 次切换 2.0 MB / 40s 空闲
28.6k 次
±0(空闲) CARenderServerCreateSnapshots 快照 + ImageIO;基线 ~715 次调用/秒

结构性规律

03purgeable 状态机层结论

hook vm_purgable_control 的 SET 调用并 onLeave 回读旧状态(内核写回),得到完整迁移矩阵:

业务0→1 置 volatile1→0 取回0→2 直接置空2→0 取回发现被偷直接调用者
快手 稳态 30s76 (4.75MB)75 (4.69MB)41sqlite3_db_release_memory ×295(页缓存 0↔1 高频往返)+ libnetwork nw_segment(0→2)
bilibili 前后台45810CA::Render::Shmem::set_volatile ×38 + sqlite ×15
SpringBoard 切换171004sqlite ×24 + CA shmem ×7 —— 快照内容被内核偷过再取回的实锤
mediaserverd 相机0000SET 全零:捕获管线纪律走 madvise(MADV_FREE_REUSABLE) ×1487/40s
流量大头是 madvise,但绝大多数不是业务在"声明可丢弃":mediaserverd 1487 次、快手 1349 次/40s 的 MADV_FREE_REUSABLE,调用者是 libmalloc free_small(空页卫生)和 JSC bmalloc::BulkDecommit(快手 web hybrid 59.5MB/40s 解提交)。业务级 purgeable 语义集中在 SQLite 页缓存、CA 层背板、网络 slab 三类。

04抓栈方法论(七步)

  1. 零注入普查先行 — ioreg/region 清点 + task_vm_info ledger(phys_footprint / purgeable / media-tag 四联表)扫全部 pid,先知道"谁重"再决定去哪采。tag 88 IOSurface 记账即在此步发现。
  2. hook 面只选公开导出符号 — malloc 8 符号(malloc_get_all_zones iOS 未导出,公开符号无双重计数);IOSurface 收敛点 IOSurfaceCreate(全部用户态路径汇聚);purgeable 7 入口(vm/mach_vm_purgable_control、madvise 过滤 advice≥5、IOSurfaceSetPurgeable、CABackingStoreSetVolatile)。设备侧先 enumerateExports 侦察存在性(malloc_create_purgeable_zone 即在侦察中确认 MISSING)。
  3. 分层采集控开销 — malloc:≥8KB 记 28 帧符号化栈、≥4KB 进存活集(free 出账 → live-by-stack 视图)、全量按 6 档计数;purgeable:SET 带栈流式、GET 只计数。JS hook 每调用 ~3-10µs,无差别回溯会触发看门狗。
  4. 协议工程对抗启动风暴 — uiopen 拉起 → +13s attach → 40-60s 窗口(过早=风暴杀 App,过晚=静态零事件);死亡分类成四型(启动风暴 / 永续分配流 / 慢启动 / 反调试);流式 send() + 周期 dump 双保险(mediaserverd 两次窗口内死亡,数据保住)。
  5. 多维校准 — malloc_zone_statistics 前后快照对账带栈覆盖率(实测 34-61%);task_vm_info ledger 前后 Δ 对账;purgeable SET 的 onLeave 回读旧状态(内核语义,源码实证)。
  6. 内核源码对齐 — xnu-7195 源码定语义再动手:purgeable 状态机/记账模型(vm_purgable.h、vm_purgeable.c:1690-1718)、mach_vm_region_recurse depth 参数真义(下降上限,传 64;depth++ 会自引用死循环)、ledger 字段偏移。
  7. 失败模式本身是结论 — 不可测的业务逐个定位机理(永续流=内容 prefetch 不停、反调试=script 层),记录为工程边界而非数据空洞;CModule 原生过滤(~100ns/次)是唯一升级路径。

05与 Instruments 的区别

✓ 本套方法能做(Instruments 做不到或很难)

  • IOKit/内核分配的申请栈:IOSurface 由内核分配,不在任何 malloc zone —— Allocations 面板看不到申请路径;hook IOSurfaceCreate 拿到全部用户态收敛点。
  • 系统 daemon:mediaserverd 相机管线(531MB+865MB 解剖)、SpringBoard 快照、backboardd —— Instruments 无法 attach 系统 daemon,越狱环境可以。
  • App Store 原版 App:无需重签名/重打包/重编译,原版二进制直接 hook,保持生产形态。
  • purgeable 状态迁移事件流:VM Tracker 只有持有量快照;这里给出 SET 迁移矩阵(谁、何时、旧→新)与"被内核偷取"实锤(2→0)。
  • 跨业务统一口径:同一 hook 面、同一协议下横向可比,栈家族分类直接当业务指纹。
  • 内核记账模型联动:ledger 普查(purgeable/media-tag/footprint)与事件流互相印证。

✗ Instruments 更强的地方(诚实交代)

  • 启动风暴窗口:native 记录开销低,能扛 10 万+/秒的启动期;JS hook 会被看门狗杀死(美团/快手稳态扛住了 11.5 万/秒,但小红书/网易云任何时点都死)。
  • 长时跟踪与回放:Instruments 的时间轴/标记/对比是成熟产品能力。
  • 反调试 App:两者都难(微信/抖音在 script 加载层挂死);Instruments 至少不需注入。
  • 图形界面分析体验:本套方法的产出是 JSON + Python 聚合,分析门槛在脚本侧。
维度Instruments (Allocations/VM Tracker)本套方法(越狱 + frida 抓栈)
malloc 用户态栈✓(需 Xcode/DDI/可调试进程)✓ 原版 App/daemon 通用
IOSurface/IOKit 申请栈✗(内核分配,不可见)✓ IOSurfaceCreate 收敛点
purgeable 状态迁移仅持有量快照✓ 迁移矩阵 + 调用者栈
系统 daemon✗✓
启动风暴✓看门狗边界(CModule 可解)
反调试 App基本不可用部分可用(IOSurface 面)
口径一致性每次配置不同同一 hook 面横向可比

06边界与升级路径

07产物清单