【Perfetto从入门到精通】2. 使用 Perfetto 追踪/分析 APP 的 Native/Java 内存

作者:Lei_official日期:2025/12/10

这个世界就是这样,你从失败中学到的东西可能比成功中学到的东西更多——《Android 传奇》

image.png

说起 Android APP 内存分析,我们第一时间想到的,往往是 Android Studio Profiler、MAT 这样的老牌工具,而 Perfetto 的出现,又为其提供了一种更加贴近底层的视角。而且相比于现有的工具,Perfetto 更加擅长于分析 Native 内存占用,可以说是补齐了工程师在这方面的短板。

在内存方向,我计划用2~3篇文章来介绍 Perfetto 的功能、特点、使用方法等 。作为第1篇,本文包含以下内容:

  • Android APP 所申请的内存分类(Java、Native)
  • 如何抓取、分析这两类内存占用情况,并提供对应的 Perfetto 操作实例

APP 占用内存分类

Android 系统框架是由 Linux 发展而来的,对于每一个应用程序(APP),框架为其初始分配一个进程,并启动了一个 ART 虚拟机(更早时候是 Dalvik),这个虚拟机用于解释执行 Java 字节码。因此,一个 APP 所占用的内存,可以归结为两类:在 Linux 层申请的 Native 内存,以及在 JVM 上申请的 Java 内存。

  • Native 内存 :由 C/C++/Rust 进程申请,其底层使用 libcmalloc()/free() 函数进行内存申请/释放。此外,APP 通过 JNI 也可申请此部分内存,例如 java.util.regex.Pattern 类的大部分功能,就同时申请了 Native 内存和 Java 内存。
  • Java 内存 :由应用内的 Java/Kotlin 代码,通过 new X() 的方式申请,位于 上,由系统自动进行回收。

针对上述内存使用场景,Perfetto 提供了两种技术进行分析。

Native (C/C++/Rust) 内存分析

在 C/C++/Rust 语言中,使用底层的 malloc/free 函数进行内存申请和释放,Perfetto 通过 AOP 技术,在这些函数内部注入代码进行拦截,因此能够识别每一次内存的细微变动。出于在应用性能和抓取效果之间保持平衡的考虑,还提供了 sampling(采样) 机制。

需要注意的是,Native 内存分析是 不可追溯 的,它只能记录从 启动 Perfetto 的时间点之后 的内存分配。这意味着,如果内存问题已经发生后,再进行 Native 内存追踪,会有无法识别引起内存问题的函数的风险(内存泄漏则不一样,因为它无时无刻不在发生)。

案例:NowInAndroid

我们知道,在 Android 7.1(API 25)之前,Bitmap 对象的像素数据是使用 Dalvik heap 存储的,在拿到 heap 文件后,可以直接导出并看到图片内容。从 Android 7.1 开始,Bitmap 的存储位置从 Dalvik heap 转移到了 Native heap。这样一来,虽然可以避免大量图片资源耗尽 JVM 内存,但也容易让人忽视掉 Native 内存管理,导致 OOM 发生。

Perfetto 的 Native 内存分析,可以用来识别这种落在 Native heap 的内存分配调用链。我们以 NowInAndroid 项目为例。

nowinandroid_homepage.png

APP 的首页是图文元素构成的瀑布流,在频繁滑动的情况下,会产生大量 Native 内存申请/回收的现象,我们通过 Perfetto 对此进行观察。

在网页端开启 Memory 目录下的 Native heap profiling 开关,同时填入待分析的包名 com.google.samples.apps.nowinandroid.demo.debug,可以通过命令 adb shell ps -A 来查看进程对应的包名。

perfetto_native_profiling.png

控制台参数如下:

1buffers {
2  size_kb: 65536
3  fill_policy: DISCARD
4}
5data_sources {
6  config {
7    name: "android.heapprofd"
8    heapprofd_config {
9      sampling_interval_bytes: 4096
10      process_cmdline: "com.google.samples.apps.nowinandroid.demo.debug"
11      shmem_size_bytes: 8388608
12      block_client: true
13      all_heaps: false
14    }
15  }
16}
17duration_ms: 10000
18

接下来点击 Start tracing 开始抓取,在 10s 的抓取过程中,上滑页面,以加载更多图片和文字。抓取结果如下:

perfetto_native_profiling_result.png

图例比较简单,分成左上角的“数据选择”、右上角的“展示方式”和占据正中央大部分的“火焰图”,依次介绍之。

数据选择”区域,共有4种排序方式,分别是:

  • Unreleased Malloc Size(未释放内存分配大小):默认模式,按未释放内存 字节数 的总和聚合调用栈。
  • Unreleased Malloc Count(未释放内存分配计数):按 计数聚合未释放的内存分配,忽略每次分配的大小。这有助于发现小规模的内存泄漏,即每个对象都很小,但随着时间的推移,大量对象会累积起来。通常称之为 内存碎片
  • Total Malloc Size(总内存分配大小):按通过 malloc() 分配的内存字节数聚合调用栈,无论这些内存是否已被释放。这有助于调查堆频繁调用,即即使最终释放了内存,也会对分配器造成很大压力的代码路径。通常称之为 内存波动
  • Total Malloc Count(总内存分配计数):与上述类似,但按 malloc() 调用次数聚合,忽略每次分配的大小。

我们目前选择的是默认的 Unreleased Malloc Size 选项。

展示方式”区域,分为“自顶向下”和“自底向上”,前者的火焰向下生长,后者反之,不赘述。

火焰图”区域,是性能分析的重点,通过层叠的形式展示函数调用链路,每个色块(Block)的面积表示该函数引起的内存增加,发生在相同层级的同一个函数,会被聚合展示。图中的火焰图可以看出,NowInAndroid 代码写的相对健壮,回收和复用都做得比较好,因此 RecyclerView 中未见内存泄漏。

Java 内存快照

在 JVM 系语言中,通过自动回收机制管理内存,大部分 Profiling 工具都是进行内存快照,计算当前虚拟机的 全部对象 及其 引用关系,从而形成一张完整的对象图。

Java 内存快照的不足之处是,无法通过它查看函数调用链路,即哪些函数引起了多大的内存增长。

perfetto_java_profiling.png

开始抓取后,上滑页面直至完成10s的抓取,以下是抓取到的结果。

perfetto_java_profiling_result.png

可以看到,Bitmap 占据了内存的大部分。此外,String数量有 96433 个,也同样值得关注。

未完待续……

参考资料


【Perfetto从入门到精通】2. 使用 Perfetto 追踪/分析 APP 的 Native/Java 内存》 是转载文章,点击查看原文


相关推荐


昨天分享了一套用 Nano Banana PRO做商业 PPT 定制的玩法,还推荐直接去咸鱼接单搞钱。
饼干哥哥2025/11/30

但有人说没有渠道、不知道怎么弄。。。 欸我还能说什么呢?只能是把做小生意的完整逻辑给大家讲一遍,包括:🧵- 怎么选择赛道? 公域流量:闲鱼实操、小红书怎么玩、公众号机会 私域谈单 SOP —、先讲一下认知:什么是 中介思维(Agent Thinking) 很多职场人或想要做副业的小白,最大的误区是觉得自己“必须先成为专家”才能赚钱。想做 PPT 代写觉得要设计大师,想做数据分析觉得要代码精通。这种思维导致你陷入技能学习的无底洞,或者单纯靠堆砌自己的时间去赚钱,不仅累,而且上限很低。一旦停下


设计模式——责任链模式实战,优雅处理Kafka消息
KD2025/12/18

一、业务背景 Kafka接收消息,需要A,B,C...多种策略做处理,再通过http请求发送给下游。多种策略混在一起很难维护,通过责任链模式把每种策略的代码收敛到自己的Handler中 二、具体设计 classDiagram Handler <|-- StrategyAHandler Handler <|-- StrategyBHandler Handler <|-- UploadHandler Handler: +void handleUserData(UserContext, Handler


卷积神经网络CNN
代码洲学长2025/12/26

CNN简介 卷积神经网络就是一个包括卷积层和池化层的神经网络,主要应用于计算机视觉方面,应用场景包括图像分类、目标检测、面部解锁、自动驾驶等。 整体架构流程 CNN的主要结构为 输入层,隐藏层 和输出层,主体架构主要体现在隐藏层中的网络,依次为卷积层 池化层 然后全连接层直接输出。CNN分别进行了两场卷积和池化 ,最终通过三个全连接层进行输出。 卷积层结构图 input(32, 32, 3) conv(3, 3, 6) relu(30, 30, 6) pool(2, 2, 6)


Elasticsearch 8.13.4 动态同义词实战全解析
detayun2026/1/4

在搜索引擎的江湖里,“词不达意"往往是阻碍用户找到心仪内容的最后一道鸿沟。当用户搜索"番茄"时,如果你的库里只有"西红柿"和"圣女果”,传统的精确匹配只能让用户空手而归。同义词库,便是那把填补语义裂痕的钥匙。然而,在 Elasticsearch 8.13.4 这个版本中,我们不再满足于重启服务来更新词库的"笨办法",我们要的是如丝般顺滑的动态热更新。 今天,我们就来一场技术突围,深度剖析在 ES 8.13.4 时代,如何玩转动态同义词,让你的搜索引擎拥有"自我进化"的灵魂。 一、 告别"文件搬运


HarmonyOS一杯冰美式的时间 -- UIUtils基础功能
猫猫头啊2026/1/13

一、前言 最近在写状态管理相关的代码,发现 HarmonyOS 的 UIUtils 这个工具类还挺实用的。它主要解决一些状态管理框架在使用过程中遇到的边界问题,比如代理对象、V1/V2 混用、数据绑定这些场景。 今天顺手整理一下它的几个核心功能,方便以后查。 该系列依旧会带着大家,了解,开阔一些不怎么热门的API,也可能是偷偷被更新的API,也可以是好玩的,藏在官方文档的边边角角~当然也会有一些API,之前是我们辛辛苦苦的手撸代码,现在有一个API能帮我们快速实现的,希望大家能找宝藏。 如果您有


LeetCode 377 组合总和 Ⅳ
展菲2026/1/21

文章目录 摘要描述题解答案题解代码分析1. 动态规划的基本思路2. 初始状态3. 状态转移方程4. 为什么这样能计算出排列个数?5. 与组合问题的区别6. 优化:避免不必要的计算 示例测试及结果示例 1:nums = [1,2,3], target = 4示例 2:nums = [9], target = 3示例 3:nums = [1,2], target = 3示例 4:nums = [1], target = 1 时间复杂度空间复杂度进阶问题:如果数组中含有负数问题分析解决方

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客