【Perfetto从入门到精通】4.使用 heapprofd 工具采样追踪 Java/Native 内存分配

你信坚持是金

也信无常命运

信天赋努力

信笨拙委屈

------ 《回声》那英

在本专栏前几篇文章里,笔者介绍过,使用 Perfetto 进行 Java Heap Dump 的方法。与通过 Android Studio Profiler 进行抓取类似,所得到的heap文件描述了应用在瞬时状态下的 堆内存分布对象引用关系 信息,对于分析内存泄漏、OOM 等问题很有帮助。与此同时,Perfetto 还提供了另一强有力的分析工具 ------ Heapprofd,它的优势在于能够 追踪 一段时间内,Native/Java 内存的全部 分配/回收 事件,从而发现是 哪些函数调用引起的内存上升

环境要求

使用 heapprofd 工具时,对于手机、APP的配置有要求

  • Android 版本:在10及以上
  • 系统构建方式
    • 如果是 debug/eng 构建:可调试所有的应用和大部分系统服务
    • 如果是 user 构建
      • 待调试应用是debug包 ------ 可以抓取
      • 待调试应用是release包 ------ 需在Manifest中声明 profileable

火焰图解读

火焰图并不复杂,甚至可以说相当直观。这是一张采样 Native 内存 的火焰图,每一块 矩形 对应一个 函数 ,其 面积大小 则意味着 该函数引起的内存分配大小。从上向下是函数的调用关系,纵向相邻的两个函数之间,上方的函数调用下方的函数。

可以选择查看不同的 Tab 的数据:

  • Unreleased malloc size
  • Total malloc size
  • Unreleased malloc count
  • Total malloc count

size 反映了内存分配总大小,count 体现出的是申请内存的次数。

进行采样的3种方式

Google 官方提供了3种进行内存分配采样的方式,它们在功能完整性、环境依赖上有所区别。

序号 方法简述 环境依赖 功能全面性
方式一 使用 adb shell perfetto 命令读取配置并执行 高,配置可编辑
方式二 python 执行 tools/heap_profile 脚本 python3 环境 高,脚本可编辑
方式三 使用 Perfetto网页工具 进行抓取 低,只能从配置预设里面选择,不支持 Java Heap Sampling

方式一:使用 adb shell perfetto 命令读取配置并执行

config.pbtx

textproto 复制代码
buffers {
  size_kb: 65536
  fill_policy: DISCARD
}
data_sources {
  config {
    name: "android.heapprofd"
    heapprofd_config {
      sampling_interval_bytes: 4096
      process_cmdline: "com.google.samples.apps.nowinandroid"
      shmem_size_bytes: 8388608
      block_client: true
      all_heaps: false
      heaps: "com.android.art"
    }
  }
}
duration_ms: 10000

对于上述配置,关键参数解释如下:

参数 说明
sampling_interval_bytes 以多少字节数的粒度进行采样
process_cmdline 要抓取的包名,(debug) or (release + profileable)
heaps com.android.art 表示采集虚拟机堆分配信息

对于 release 包,在 AndroidManifest.xml 中声明 profileable

xml 复制代码
<manifest ...>
    <application>
        <profileable android:shell="true"/>
        ...
    </application>
</manifest>

当然也可以增加想要抓取的其它信息作为配置,随后通过 adb shell perfetto 命令启动抓取,抓取完成后,将 trace.pftrace 文件pull到本地进行分析。

抓取命令

bash 复制代码
cat config.pbtx | adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/trace.pftrace

方式二:python执行tools/heap_profile脚本

该脚本位于 github.com/google/perf... ,用参数 processName(-n com.google.samples.apps.nowinandroid)或者 PID(-p 1234)指明要抓取的进程。推荐使用 进程名 的方式,可以在进程启动前,就开始抓取任务(当然,进程尚未启动时,我们是不知道其 PID 的)。抓取到的数据保存为 raw-trace,可以用 Perfetto UI 查看。

方式三:使用 Perfetto UI 进行抓取

这种方式无法抓取 Java 内存分配信息,不推荐。

采样 Native 内存

对"采样"的理解

既然是"采样",说明并不是完整记录了全部的内存分配事件,而是 有选择地 。在 Perfetto 中,采样的粒度取决于 sampling_interval_bytes 参数,翻译过来就是 采样区间字节数,其默认值是 4KB,表示 每当内存累计分配超过4KB时,就记录这一次内存申请的函数调用

通过使用这种"按字节流抽样"的方法,结合 Poisson sampling(泊松抽样),可以让统计更加稳健,既能覆盖高频小分配,也不会漏掉低频大分配

在目标进程里,heapprofd 拦截了 libcmalloc/free 家族调用。

采样 Java 内存

Java Heap Sampling 是一种 抽样式Java 对象分配记录机制,用于观察内存分配趋势。它记录的是

  • Java 对象分配事件(allocation)
  • 对象类型(class)
  • 分配大小
  • 时间
  • 线程

一个典型的 Java 内存采样火焰图如下所示:

如何启用它

  • 对于"方式一 ",在配置文件中增加 heaps: "com.android.art(上文的配置文件已经增加)
  • 对于"方式二 ",执行 python 脚本时,增加 --heaps com.android.art 参数
  • 对于"方式三",不支持

它会做什么

它抽样记录了一段时间内对象分配的数量和大小,与 Native Heap 类似,但发生在 ART 层。

它不会做什么

它不会扫描整个 Java Heap,也不会构建完整的 对象引用关系表 。------这是 heapdumpAS Profiler 的职责。

它不会 记录对象的回收和销毁 ,这是 ART 虚拟机 自动执行的,Perfetto 无法介入。

不知道什么原因,在同时开启 Java Heap Sampling 和 Java Heap Dumps 时,我只能在结果里得到前者,无法看到后者。

使用Perfetto分析内存问题的框架

参考资料

相关推荐
alexhilton3 小时前
学会在Jetpack Compose中加载Lottie动画资源
android·kotlin·android jetpack
苏打水com3 小时前
第十四篇:Day40-42 前端架构设计入门——从“功能实现”到“架构思维”(对标职场“大型项目架构”需求)
前端·架构
summerkissyou19874 小时前
Android-Camera-为啥不移到packages/module
android·相机
liang_jy4 小时前
Android UID
android·面试
自由生长20244 小时前
从Web网站回退到从命令行:用领域驱动设计构建软件最关键的业务内核
架构
Nandeska4 小时前
1、全面理解MySQL的架构
架构
咨询qq 8762239655 小时前
三种改进措施改进蜣螂优化(DBO)算法 1,Chebyshev映射总群初始化 2
性能优化
G31135422735 小时前
Linux 内核设计中的核心思想与架构原则
linux·架构·php
AI_56786 小时前
Webpack5优化的“双引擎”
大数据·人工智能·性能优化