内容提要
heapprofd 是 Perfetto 的 native 内存采样工具,将 malloc/new 的分配、释放及调用栈写入 Trace,用于定位 native heap 增长来源。它不替代 Java heap dump,也不覆盖 GraphicBuffer、dma-buf、GPU 等图形内存。使用流程:先用 RSS/PSS 确认趋势,再按进程名或 PID 抓取,通过 Heap Profile 火焰图查看未释放量与调用栈,并用 SQL 聚合归因。需注意采样间隔、符号化、buffer 丢失和 profileable 权限,结论应区分泄漏、峰值、缓存与 churn。
延伸解读
heapprofd 的边界:它只回答 malloc/new 调用栈
heapprofd 统计的是目标进程向 libc malloc/free、new/delete 请求的字节数,不等于 RSS,也不覆盖 GraphicBuffer、dma-buf、GPU texture、驱动侧分配或直接 mmap/memfd/ashmem。基于 malloc 的 arena 可能只看到大块,看不到内部对象。自研 allocator 需通过 custom allocator API 注册自定义 heap 才能纳入。图形内存应优先查 meminfo Graphics/GL、dmabuf、GPU/gra
采样间隔与数据丢失:别把采样结果当精确账本
sampling_interval_bytes: 4096 表示平均每分配 4096 字节采一个样本,再按概率归因到调用栈,并非低于该值就估算、高于就精确。4096 是常用起点,小对象密集时可降低间隔但开销上升;分配速率过高导致 buffer overrun 时,应先增大 shared memory 或提高采样间隔。手写配置必须显式设置非 0 采样间隔,0 无效且 Android 12 前可能使目标进程崩溃。报告应写“在 4096 字节采样间隔下,未释放分配主要集中在某几条调用栈”,而非精确分配量。
泄漏判断需要三个时间点,不能只看单个 diamond
continuous dump 的每个 diamond 是从记录开始到该时间点的累计结果。判断泄漏至少要看操作前、操作后、释放后三个点:操作后上涨且释放后仍不降,才偏泄漏;操作中升高但释放后回落是峰值;长期保留但有容量上限、复用证据和压力回收策略才可能写成缓存。实验室可用 adb shell killall -USR1 heapprofd 额外触发 snapshot,把业务动作与 diamond 对齐。allocator cache 和碎片可能导致 heapprofd 数字下降而 RSS 不立刻下降。
报告可信度:符号化、stats 与结论分类
火焰图出现地址、unknown 或混淆名时,需用 traceconv bundle 补 native symbols 和 ProGuard/R8 mapping,并记录 APK/build id 与符号版本;Build ID mismatch 时降级为地址级证据。必须检查 heapprofd stats、stackprofile stats、symbolization stats,命中 overrun、non-finalized、unknown symbols 时降级。结论应归类为 leak、peak、cache、
Q&A
heapprofd 是什么?它能解决哪些内存问题?
heapprofd 是 Perfetto 的 native 内存采样工具,将 malloc/new 的分配、释放及调用栈写入 Trace,用于定位 native heap 增长来源。它适合在 RSS/PSS 趋势确认后,查找 native allocation 调用栈,例如 JNI/C++、Skia/Bitmap 的 CPU 侧分配、播放器 native wrapper 等问题。它不替代 Java heap dump,也不覆盖 GraphicBuffer、dma-buf、GPU 等图形内存。
heapprofd 和 Java heap dump 有什么区别?
heapprofd 统计 native 分配(libc malloc/free、new/delete)的调用栈,回答“谁分配了 native 内存”;Java heap dump 展示 Java 对象保留关系,回答“谁持有 Java 对象”。heapprofd 不替代 Java heap dump,两者针对不同的内存账。
如何用命令行抓取 native heap profile?
官方推荐使用 tools/heap_profile 脚本,例如:tools/heap_profile -n com.example.app -d 15000 -i 4096 -c 5000 -o /tmp/heapprofd-com-example-app。该命令按进程名抓 15 秒,采样间隔 4096 bytes,每 5 秒产生一个 continuous dump。输出目录包含 raw-trace 和转换后的 profile 文件,可拖入 Perfetto UI 查看 Heap Profile diamond。
heapprofd 的采样间隔是什么意思?如何选择?
sampling_interval_bytes: 4096 表示平均每分配 4096 bytes 采一个样本,再按采样概率归因到调用栈。4096 bytes 是常用起点;怀疑大量小对象分配时可降低间隔,但开销上升;分配速率太高导致 buffer overrun 时,先增大 shared memory 或提高采样间隔。手写配置必须显式设置非 0 采样间隔,0 无效且 Android 12 前可能崩溃。
如何用 SQL 查询未释放的 native 分配?
heapprofd 写入 heap_profile_allocation 等表。可先用官方标准库 android.memory.heap_profile.summary_tree 聚合整条调用栈,查询 cumulative_size 排序。若需按进程和 dump 时间点过滤,可回到原始表,用 SUM(a.size) 计算截至 dump 结束的保留量,注意 heap_profile_allocation 存储增量,需累加 ts <= dump_ts,并只统计 libc.malloc 避免混入 com.android.art。
heapprofd 的结论如何区分泄漏、峰值、缓存和 churn?
泄漏:操作后未释放量增加,释放后仍不降;峰值:操作中升高但释放后回落;缓存:长期保留但有容量上限、复用证据和压力回收策略;churn:Total malloc size 高但 Unreleased malloc size 不高,即频繁申请快速释放。判断泄漏至少要看操作前、操作后、释放后三个点,不能凭单个 diamond 下结论。
使用 heapprofd 需要注意哪些权限和限制?
heapprofd 需要 Android 10 或更高版本;Java allocation sampling 需要 Android 12 或更高版本。user build 上用 adb/shell 抓 App 通常需要 debuggable 或 <profileable android:shell="true"/>。按 PID 抓可能错过启动早期分配,按进程名可覆盖更多启动阶段。同一目标进程同时只能被一个 session profile。32-bit 程序在部分旧版本有限制。
heapprofd 能分析图形内存吗?
不能。heapprofd 默认只统计 libc malloc/free 等 allocator 请求的字节数,不覆盖 GraphicBuffer、dma-buf、GPU texture、驱动侧分配、直接 mmap/memfd/ashmem 或绕过 libc malloc 的 allocator。图形内存应通过 meminfo Graphics/GL、dmabuf、GPU/graphics 计数、SurfaceFlinger 等分析,heapprofd 仅用于排除 App/native wrapper 是否也在分配。