点击左侧任意节点查看详情
环 = 层级深度(内→外)· 弧长 = 子树节点数 · 颜色 = ArkUI 类型分组(■容器布局 ■内容呈现 ■交互输入 ■导航容器 ■其他,越深越暗)· M2 堆普查/M5 组件内存在 ArkUI 无等价通道(对象计数进程外不可达),故本站只有「计数」度量——这是与 iOS 站(四度量切换)的诚实差异 · 悬停看子树规模,点击查节点详情
点击旭日图任意环段
悬停查看详情
| 事件 | iOS(实测 ✓) | 鸿蒙(文档级 ⏳) |
|---|---|---|
| 组件上树 | 布局 ✓ / 解码 ✗ / 绘制 ✗ 86% 层 contents=nil(bili 1,163 层实测:996 层零像素) |
布局 ✓ / 解码 ✓ / 绘制 ✗ createPixelMap 即解码,与可见性无关 |
| 上树但不可见 | 零像素成本(惰性红利:仅压缩数据 0.86MB/张,tag-88 全程 0 实证) | 解码内存已付,直到 LRU 逐出 |
| 首次可见 | 此时才解码 + 纹理上传 代价:首帧主线程解码 → 大图掉帧 |
直接用已解码位图 红利:首帧就绪不卡 |
| 属性动画 | 只合成(backboardd 服务端处理,不回 App) | 只合成(RS 处理,架构同构) |
| 内容失效重绘 | setNeedsDisplay → 下个 runloop turn 的 drawRect/display | 标记脏 → 下个 VSync 重绘(Canvas/CustomNode 才有 App 侧光栅化) |
LazyForEach 控制列表项组件要不要创建)——用「不创建组件」间接实现「不解码」,粒度更粗、由应用负责。
| 维度 | iOS | 鸿蒙 |
|---|---|---|
| 像素生产地点 | App 进程内(CPU:drawRect → CABackingStore 堆,92 层实测) | RS 进程(GPU:矢量/文本栅格化在此,App 只录显示列表) |
| 图片解码时点 | 第④步 display 首次需要时(惰性,四点实验实证) | 第①步组件创建时(eager,解码提前三个阶段) |
| 隐藏视图语义 | 仍参与布局 · 跳过像素生产 · 不合成 | Hidden 占位不渲染 · None 连布局都退出 |
| 重复出现 | 两端一致:不重画——一次生产 · 缓存复用 · 标脏才重画("可见后绘制"的准确说法是"渲染需求驱动") | |
| 合成者 | backboardd(render server) | RS / Composer(角色等价物) |
| App 角色 | 像素生产者(交位图) | 指令录制者(交显示列表) |
| 负载 | iOS(lazy + purgeable) | 鸿蒙(eager + DMA + LRU) | 胜者 |
|---|---|---|---|
| 常显示、反复用的图 | 堆位图 + server 纹理 = 系统两份 | DMA 一份全系统共享 · 缓存命中不重解码 | 鸿蒙 |
| 大量隐藏/预建图 (bili 首页 63.2% 隐藏类负载) | 仅压缩数据 0.86MB/张 | 默认解码 48MB × N,直到 LRU 上限 | iOS(量级差) |
| 内存压力行为 | purgeable 静默回收 · 不杀进程 | LRU 逐出 · 256MB 内持久占用,靠应用调参 | iOS |
| 首帧延迟 | 首次绘制主线程解码 → 大图掉帧 | 已就绪 → 流畅 | 鸿蒙 |
| # | 原论证 | 检验结果 |
|---|---|---|
| 修正 ① | 「鸿蒙支持 lazy 可选,所以更优」 | 不成立——两端对称:iOS 可 predecode 转 eager(iOS 15+ preparingForDisplay),鸿蒙可 DIY 转 lazy。差别只是默认值方向,不是能力 |
| 修正 ② | 「iOS 需从 ImageIO 拷贝到 IOSurface」 | 机制说反半步——拷贝是「App 堆 → server 纹理」的一次性上传(server 缓存复用,非每帧);且 iOS 视频路径(CVPixelBuffer)/Metal 本就零拷贝,「必须拷贝」只对静态图成立。实测:解码产物 tag-88 全程 0,从头到尾不是 IOSurface |
| 修正 ③ | 「PixelMap 走 DMA 零拷贝」 | 设计上限 ≠ 默认现实——硬解路径落 DMA,软解/部分格式仍可能堆 PixelMap + 上传;GPU 采样 dma-buf 还受格式/stride 兼容约束(文档级,待实测) |
task_conversion_eval ipc_tt.c:2183)——「全系统总量」对比在 iOS 侧只能推算不能实测;鸿蒙侧图片管线细节仍为文档级推断(设备已就位,实验待做)。| 组 | 配置 | 验证什么 | 状态 |
|---|---|---|---|
| A | 鸿蒙默认 Image + 隐藏(Visibility.None) | eager 基线:隐藏态是否就吃 48MB 解码内存(hidumper --mem 查 PSS/dma_heap) | ⏳ 设备已就位,待做 |
| B | 鸿蒙 LazyForEach 屏外 item | 组件级懒的边界:组件不创建是否即不解码 | ⏳ 设备已就位,待做 |
| C | 鸿蒙 DIY:持 ImageSource + 可见才 createPixelMap | 应用级懒 = iOS 语义复刻(四点法直接套用) | ⏳ 设备已就位,待做 |
| D | iOS 默认(隐藏图片组件) | lazy 基线:隐藏态只有压缩数据 | ✓ 已测(tag-88=0 · 压缩 0.86MB) |
| E | iOS predecode(offscreen 强制绘制) | eager 形态复刻:验证「两端默认值不同、能力对等」 | ⏳ 可立即做 |
raw-data/exp-decode-2026-09-24.md · 解码触发时点 = CA 周期 layout → display(解码在此) → commit(运输)
| 指标 | iOS 通道(已验证) | 鸿蒙通道(本次实测) | 状态 |
|---|---|---|---|
| M1 组件树 | debugserver 重签直连 + lldb recursiveDescription(挂起态) | hdc shell uitest dumpLayout(框架自省) | ✓ 双通 |
| M3 类型直方图 | uicount 堆普查按类计数 | dump 的 type 属性直接聚合 | ✓ 双通(口径不同:类 vs 组件类型) |
| M4 内存 | mach task_vm_info(phys_footprint/internal)+ TAGHIST | hidumper --mem(PSS 分项 + swap + heap) | ✓ 双通(口径:footprint vs PSS) |
| M6 可见性 | 静态分类器(hidden 传播/裁剪/离屏/遮挡启发式) | 框架 visible 属性(真值)+ bounds 离屏判定 | ✓ 双通(鸿蒙侧是框架真值,更强) |
| M2 存活对象计数 | uicount 堆扫描(255,737 ObjC 对象实名) | — | ✗ 无等价通道(ArkUI 对象计数进程外不可达) |
| M5 组件内存归因 | class_ro_t.instance_size 全类表 join | — | ✗ 依赖类尺寸表,同因不可达 |
HDC=~/.dsh/skills/ohos-device-lab/bin/hdc "$HDC" shell 'aa start -a EntryAbility -b com.tencent.wechat' # 启动(需显式 ability 名) "$HDC" shell 'uitest dumpLayout -p /data/local/tmp/ohos_uicount/x.json' # 组件树 dump(≈M1) "$HDC" shell 'uitest screenCap -p /data/local/tmp/ohos_uicount/x.jpg' # 同时刻截图 "$HDC" shell 'hidumper --mem <pid>' # 内存账单(≈M4) "$HDC" shell 'hidumper -s WindowManagerService -a "-a"' # 窗口/z序(pid/焦点)