Android Perfetto 系列 14:heapprofd 与内存 Profiling
- 4096 bytes 是常用起点,适合大多数本地分析。
- 怀疑大量小对象 native 分配时,可以降低间隔,但开销会上升。
- 分配速率太高导致 buffer overrun 时,先增大 shared memory 或提高采样间隔。
- 手写
HeapprofdConfig必须显式设置非 0 的采样间隔;想接近精确可以设 1,但开销很高。0 是无效配置,不应作为“關閉采样”的写法。
报告里不要把采样结果写成“精确分配了 12.34MB”。更合适的写法是:“在 4096 bytes 采样间隔下,未释放分配主要集中在 A/B/C 三條调用栈,A 调用栈占比最高”。采样 profiler 的价值是找方向和排序,不是替代 allocator 账本。
小对象密集、样本少、窗口短、调用栈分散时,top stack 排名会不稳定。before/after 比较必须保持同一采样间隔、相近負载和同一方向窗口;不要比较 1% 或 2% 这类小差异,除非多次复现。报告里的 evidence_grade 可以按这个规则写:样本充足、连续 dump 走向一致、符号完整为 strong;命中 data loss、profile incomplete、unknown symbols、样本少或只复现一次为 weak。weak 时写“候选调用栈”,不要写“根因调用栈”。
Java heap dump 和 Java allocation sampling
这部分是对开头几张内存账的补充:heapprofd 能看 native malloc/new,也能在 Android 12 或更高版本上切到 Java allocation sampling,但它仍不替代 Java heap dump。
| 能力 | 输出 | 适合议题 |
|---|---|---|
| heapprofd native profiling | malloc/new 调用栈 | 谁分配了 native 内存 |
| Java heap dump | 对象保留图 | 谁持有 Java 对象 |
| Java allocation sampling | Java 创建对象的调用栈 | 哪些代码制造了大量 Java allocation churn |
Java allocation sampling 可以通过 heapprofd 配置 heaps: "com.android.art" 打开,也可以用脚本参数 --heaps com.android.art。它需要 Android 12 或更高版本。它记录对象创建时的调用栈,不记录对象什么时候被 GC,也不等价于 Java heap dump 的保留关系。
符号化和可信度
如果 flamegraph 里都是地址、unknown 或混淆后的 Java/Kotlin 名字,先处理符号化和反混淆。官方现在值得一试对采集到的 Trace 运行 trace_processor bundle 生成带符号信息的归档,再按符号化文档补 native symbols 和 ProGuard/R8 mapping。
报告里要记录 APK/build id、native symbols 版本、ProGuard/R8 mapping 版本;Build ID mismatch 时降級为地址级证据。第三方 SDK 或 stripped so 只剩地址时,下一步是补对应 build 的 unstripped symbols / mapping,而不是直接改业务代码。
读调用栈时也不要盲目过滤 libart.so、allocator glue 或操作系统库。ART 帧有时正是 Java 调 native、NativeAllocationRegistry 或对象创建路径的关键上下文。
先找 allocator/ART glue 之上的第一个 App、SDK 或业务帧;只有确认某类帧只是重复胶水层时再过滤。看到 DEDUPED、ICF 造成的函数折叠、Build ID mismatch、只有一帧等情况,要先按符号化议题处理。
unwind 可信度也要看:未知帧比例、单帧栈比例、Build ID 是否匹配、Java frame 是否可用、目标 ABI / Android 版本 known issues。缺符号会改变归因可信度;报告里要区分“已符号化可归因”和“只能定位到 so / allocator glue”。
报告里至少要交代清楚:
- 方向進程是否 profileable/debuggable。
sampling_interval_bytes和 continuous dump 间隔。stats里是否出现 heapprofd buffer overrun、client error、profile incomplete、heapprofd_unwind_time_us、heapprofd_client_spinlock_blocked、heapprofd_sampling_interval_adjusted或 data loss。
出现 data loss 或 heapprofd buffer overrun 时,不要把数字写成精确账本。更合适的说法是“该调用栈是未释放分配的大头,采样條件如下”。
通用的 Trace 体检(buffer/ftrace 丢失)直接用第 12 篇那條 stats 查询;heapprofd 专用 health check 要单独跑。heapprofd_buffer_overran、heapprofd_client_error、heapprofd_hit_guardrail 等在 v57.2/v58.2 已存在,多数属于按需生成记录的 indexed stats;在没有 heap profile 的 Trace 上只查到两个 single 项,不能推断其他项是新版本专属。
先确认方向進程/heap 的 profile 或 dump 确实存在、窗口覆盖且已完成,再解释下面的异常查询。没有 profile 时应标记 profile_unavailable,不是“全部干净”;有 profile 但异常查询为空,也只能說明未记录到这些异常。heapprofd_unwind_time_us、heapprofd_client_spinlock_blocked、heapprofd_sampling_interval_adjusted 是开销/采样上下文,不应与非零 error/data_loss 一律判失败。
heapprofd_last_profile_timestamp 是辅助的最后 profile packet 時間,不能替代 heap_profile.ts_end、diamond 和最终完成狀態来证明方向窗口完整。下列查询保留这些上下文供复查:
本文由News整理發布,僅供參考。

