Android Perfetto 系列 14:heapprofd 与内存 Profiling
实验室 leak 复现可以设 block_client: true 并配 block_client_timeout_us,用目标進程 malloc 路径的阻塞换完整性;交互性能分析不要默认开,因为它可能反向作用 jank/latency。交互场景通常用 block_client: false,再通过更高采样间隔、更大 shmem_size_bytes 和 stats 检查接受不完整风险:必须检查 heapprofd_buffer_overran、heapprofd_non_finalized_profile、heapprofd_last_profile_timestamp 是否覆盖方向窗口。
把 heapprofd 放進系统 Trace 时,意见同时保留少量业务 marker。App 内用 android.os.Trace / AndroidX Trace 会落到 atrace;Perfetto SDK Track Event 需要单独启用 track_event data source。
比如播放器场景至少要有 operation_start、steady_state、operation_end、release_done/gc_idle_done 四类 marker。没有 launch marker 时,结论只能写“操作后保留增加”,不能写“泄漏”。官方文档将 android.log 支持范围写为 userdebug;已 root 且 logd/SELinux 權限允许的 user 构建也可能采到,但不能仅凭 adb root 推广到所有设备。普通生产 user build 上不要把 logcat 当成这份配置默认能抓到的证据。
UI 怎么看 Heap Profile
导入 trace 后,在目标進程下找 Heap Profile 轨道。轨道上的 diamond 代表一次 dump。点击 diamond 后,底部会展示 flamegraph。
continuous dump 的每个 diamond 都是从记录开始到该时段点的累计结果。先看未释放量判断占住不放,再看总分配量判断 churn。读图时按这个顺序走:
- 看连续几个 diamond 的
Unreleased malloc size是否持续增加。 - 看
Total malloc size,判断是不是分配 churn 很高但释放也快。 - 用 Left Heavy 视图找最大的调用栈。
- 先找 allocator / ART glue 之上的第一个 App、SDK 或业务帧。
- 回到時間轴,关联触发操作、业务 Track Event、线程运行。

示意图(假设统计结果):连续 dump 的未释放量为 2→5→4 MiB;火焰图用于查看调用栈贡献,不能凭单点判泄漏。
不要用单个 diamond 判泄漏。泄漏判断至少要看“操作前、操作后、回收后”几个点。如果操作后上涨,回收后仍然不降,再看未释放调用栈。
实验室里可以用 adb shell killall -USR1 heapprofd 额外触发一次 snapshot。比如“進入页面前、页面稳定后、登出页面并完成 debut/close/析构后”各触发一次,比只靠固定 5 秒间隔更容易把业务动作和 diamond 对上。
只有当 native backing 由 Java wrapper、NativeAllocationRegistry 或 Cleaner/finalizer 持有时,才需要把“等待 idle、finalizer、GC 完成”算進这个狀態点。allocator cache 和碎片还可能导致 heapprofd 数字回落了、RSS 却不立刻下降。缓存不是“不释放”的委婉说法,必须有容量上限、命中/复用证据、登出或压力下回收策略;否则只能写“疑似长期保留”。
SQL 怎么查未释放分配
heapprofd 会写入调用栈相关表:
| 表 | 含义 |
|---|---|
heap_profile_allocation | allocation/free 的样本和大小 |
stack_profile_callsite | 调用栈节点 |
stack_profile_frame | 函数帧 |
stack_profile_mapping | so、apk、jar、binary 映射 |
stack_profile_symbol | 離線符号化结果 |
单目标 profile 快速读图时,优先用官方标准库里的 summary tree。它会沿着 stack_profile_callsite.parent_id 聚合整條调用栈,结果更接近 flamegraph 里的 cumulative view:
1 | INCLUDE PERFETTO MODULE android.memory.heap_profile.summary_tree; |
summary tree 汇总整份 Trace 中所有進程、heap 和時間的记录;本例只适合仅采单進程 native heap 时查看末尾累计狀態,不是任意 diamond 的快照。它没有進程列,不要直接用于多進程回归统计。多進程 App、:remote 進程或同名進程重启时,要回到原始表按 upid、PID、進程名和 diamond 時間点过滤,避免把别的進程归到目的進程。
如果要按進程、diamond 时刻点和 leaf frame 做粗查,再回到原始表。这條 SQL 计算截至最后一个 dump 结束時間点的保留量,重点看 SUM(a.size):allocation 是正数,free 是負数,求和后更接近该 diamond 上的 Unreleased malloc size。它不用于最终归因,最终报告必须回到完整调用栈。这里的 heap_profile_allocation 存储每次 dump 相对上次的增量,不是每个 dump 的完整快照;必须累加 ts <= dump_ts。以下只统计 libc.malloc,避免把不会记录 free 的 com.android.art 混成未释放 native 内存。
本文由News整理發布,僅供參考。

