android studio 使用 Memory Profiler 查看 Java 堆和内存分配(一)

转载 https://developer.android.google.cn/studio/profile/memory-profiler#profiler-memory-leak-detection?utm_source=androidweekly.io&utm_medium=website

使用 Memory Profiler 查看 Java 堆和内存分配

Memory Profiler 是 Android Profiler 中的一个组件,可帮助您识别可能会导致应用卡顿、冻结甚至崩溃的内存泄露和内存抖动。它显示一个应用内存使用量的实时图表,让您可以捕获堆转储、强制执行垃圾回收以及跟踪内存分配。

要打开 Memory Profiler,请按以下步骤操作:

  1. 依次点击 View > Tool Windows > Profiler(您也可以点击工具栏中的 Profile 图标 )。
  2. 从 Android Profiler 工具栏中选择要分析的设备和应用进程。如果您已通过 USB 连接设备但系统未列出该设备,请确保您已启用 USB 调试。
  3. 点击 MEMORY 时间轴上的任意位置以打开 Memory Profiler。

或者,您可以从命令行使用 dumpsys 来检查您的应用内存,还可以在 logcat 中查看 GC 事件。

为什么应分析您的应用内存

Android 提供了托管内存环境 - 当它确定您的应用不再使用某些对象时,垃圾回收器会将未使用的内存释放回堆中。虽然 Android 查找未使用内存的方式在不断改进,但对于所有 Android 版本,系统都必须在某个时间点短暂地暂停您的代码。大多数情况下,这些暂停难以察觉。不过,如果您的应用分配内存的速度比系统回收内存的速度快,则当回收器释放足够的内存以满足您的分配需要时,您的应用可能会延迟。此延迟可能会导致您的应用跳帧,并使系统明显变慢。

尽管您的应用不会表现出变慢,但如果存在内存泄露,则即使应用在后台运行也会保留该内存。此行为会强制执行不必要的垃圾回收事件,因而拖慢系统其余部分的内存性能。最后,系统被迫终止您的应用进程以回收内存。然后,当用户返回您的应用时,它必须完全重启。

为帮助防止这些问题,您应使用 Memory Profiler 执行以下操作:

  • 在时间轴上查找可能会导致性能问题的不理想的内存分配模式。
  • 转储 Java 堆以查看在任何给定时间哪些对象耗尽了内存。在很长一段时间内进行多次堆转储有助于识别内存泄露。
  • 记录正常用户交互和极端用户交互期间的内存分配,以准确识别您的代码在何处短时间内分配了过多对象,或分配了泄露的对象。

如需了解可减少应用内存使用量的编程做法,请阅读管理应用内存。

Memory Profiler 概览

当您首次打开 Memory Profiler 时,您将看到一条表示应用内存使用量的详细时间轴,并可使用各种工具来强制执行垃圾回收、捕获堆转储以及记录内存分配。

图 1. Memory Profiler
图 1. Memory Profiler

如图 1 所示,Memory Profiler 的默认视图包括以下各项:

  1. 用于强制执行垃圾回收事件的按钮。

  2. 用于捕获堆转储的按钮。

    注意:只有在连接到搭载 Android 7.1(API 级别 25)或更低版本的设备时,才会在堆转储按钮右侧显示用于记录内存分配的按钮。

  3. 用于指定分析器多久捕获一次内存分配的下拉菜单。选择适当的选项可帮助您在分析时提高应用性能。

  4. 用于缩放时间轴的按钮。

  5. 用于跳转到实时内存数据的按钮。

  6. 事件时间轴,显示活动状态、用户输入事件和屏幕旋转事件。

  7. 内存使用量时间轴,它会显示以下内容:
    一个堆叠图表,显示每个内存类别当前使用多少内存,如左侧的 y 轴以及顶部的彩色键所示。
    一条虚线,表示分配的对象数,如右侧的 y 轴所示。
    每个垃圾回收事件的图标。

不过,如果您使用的是搭载 Android 7.1 或更低版本的设备,则并非所有分析数据在默认情况下都可见。如果您看到一条消息,显示“Advanced profiling is unavailable for the selected process”,您需要启用高级分析才能看到以下内容:

  • 事件时间轴
  • 分配的对象数
  • 垃圾回收事件

在 Android 8.0 及更高版本上,始终为可调试应用启用高级分析。

如何计算内存

您在 Memory Profiler 顶部看到的数字(图 2)基于您的应用根据 Android 系统机制所提交的所有私有内存页面。此计数不包含与系统或其他应用共享的页面。
在这里插入图片描述图 2. Memory Profiler 顶部的内存计数图例

内存计数中的类别如下:

  • Java:从 Java 或 Kotlin 代码分配的对象的内存。

  • Native:从 C 或 C++ 代码分配的对象的内存。

    即使您的应用中不使用 C++,您也可能会看到此处使用的一些原生内存,因为 Android 框架使用原生内存代表您处理各种任务,如处理图像资源和其他图形时,即使您编写的代码采用 Java 或 Kotlin 语言。

  • Graphics:图形缓冲区队列向屏幕显示像素(包括 GL 表面、GL 纹理等等)所使用的内存。(请注意,这是与 CPU 共享的内存,不是 GPU 专用内存。)

  • Stack:您的应用中的原生堆栈和 Java 堆栈使用的内存。这通常与您的应用运行多少线程有关。

  • Code:您的应用用于处理代码和资源(如 dex 字节码、经过优化或编译的 dex 代码、.so 库和字体)的内存。

  • Others:您的应用使用的系统不确定如何分类的内存。

  • Allocated:您的应用分配的 Java/Kotlin 对象数。此数字没有计入 C 或 C++ 中分配的对象。

    如果连接到搭载 Android 7.1 及更低版本的设备,只有在 Memory Profiler 连接到您运行的应用时,才开始此分配计数。因此,您开始分析之前分配的任何对象都不会被计入。不过,Android 8.0 及更高版本附带一个设备内置分析工具,该工具可跟踪所有分配,因此,在 Android 8.0 及更高版本上,此数字始终表示您的应用中待处理的 Java 对象总数。

与以前的 Android Monitor 工具中的内存计数相比,新的 Memory Profiler 以不同的方式记录您的内存,因此,您的内存使用量现在看上去可能会更高些。Memory Profiler 会监控一些额外的类别,这就增加了总的内存使用量,但如果您仅关心 Java 堆内存,则“Java”项的数字应与以前工具中的数值相似。 然而,Java 数字可能与您在 Android Monitor 中看到的数字并非完全相同,这是因为新数字计入了自应用的 Java 堆从 Zygote 派生以来为其分配的所有物理内存页面。因此,它可以准确反映您的应用实际使用了多少物理内存。

注意:使用搭载 Android 8.0(API 级别 26)及更高版本的设备时,Memory Profiler 还会显示应用中的一些误报的原生内存使用量,而这些内存实际上是分析工具使用的。对于大约 100000 个对象,最多会使报告的内存使用量增加 10MB。在 IDE 的未来版本中,这些数字将从您的数据中过滤掉。

查看内存分配


内存分配为您显示内存中的每个 Java 对象和 JNI 引用是如何分配的。具体而言,Memory Profiler 可为您显示有关对象分配的以下信息:

  • 分配了哪些类型的对象以及它们使用多少空间。
  • 每个分配的堆栈轨迹,包括在哪个线程中。
  • 对象在何时被取消分配(仅当使用搭载 Android 8.0 或更高版本的设备时)。

如果您的设备搭载的是 Android 8.0 或更高版本,您可以随时查看对象分配,具体操作步骤如下:在时间轴上拖动以选择要查看哪个区域的分配。不需要开始记录会话,因为 Android 8.0 及更高版本附带设备内置分析工具,可持续跟踪您的应用分配。

如果您的设备搭载的是 Android 7.1 或更低版本,请点击 Memory Profiler 工具栏中的 Record memory allocations 图标 。记录时,Memory Profiler 会跟踪您的应用中发生的所有分配。完成后,请点击 Stop recording 图标 以查看分配。

选择时间轴的某个区域后(或者使用搭载 Android 7.1 或更低版本的设备完成记录会话后),已分配对象的列表将显示在时间轴下方,按类名称进行分组,并按其堆计数排序。

要检查分配记录,请按以下步骤操作:

  1. 浏览列表以查找堆计数异常大且可能存在泄露的对象。为帮助查找已知类,点击 Class Name 列标题以按字母顺序排序。然后,点击一个类名称。此时右侧将出现 Instance View 窗格,显示该类的每个实例,如图 3 所示。

    此外,您也可以快速找到对象,方法是点击 Filter 图标 ,或按 Ctrl+F 键(在 Mac 上,按 Command+F 键),然后在搜索字段中输入类或软件包名称。如果从下拉菜单中选择 Arrange by callstack,还可以按方法名称搜索。如果要使用正则表达式,请勾选 Regex 旁边的复选框。如果您的搜索查询区分大小写,请勾选 Match case 旁边的复选框。

  2. 在 Instance View 窗格中,点击一个实例。此时下方将出现 Call Stack 标签页,显示该实例被分配到何处以及在哪个线程中。

  3. 在 Call Stack 标签页中,右键点击任意行并选择 Jump to Source,以在编辑器中打开该代码。

在这里插入图片描述
您可以使用已分配对象列表上方的两个菜单来选择要检查的堆以及如何组织数据。

从左侧的菜单中,选择要检查的堆:

  • default heap:当系统未指定堆时。
  • image heap:系统启动映像,包含启动期间预加载的类。此处的分配保证绝不会移动或消失。
  • zygote heap:写时复制堆,其中的应用进程是从 Android 系统中派生的。
  • app heap:您的应用在其中分配内存的主堆。
  • JNI heap:显示 Java 原生接口 (JNI) 引用被分配和释放到什么位置的堆。

从右侧的菜单中,选择如何安排分配:

  • Arrange by class:根据类名称对所有分配进行分组。这是默认选项。
  • Arrange by package:根据软件包名称对所有分配进行分组。
  • Arrange by callstack:将所有分配分组到其对应的调用堆栈。
### Android Studio Profiler 使用教程:检测内存变化 #### 工具概述 Android StudioProfiler款强大的工具,能够帮助开发者监控优化应用程序的性能。其中,内存分析器是个重要的组成部分,可以用于识别内存泄漏、内存溢出等问题,并提供实时的应用程序内存使用情况图表[^2]。 #### 启动 Profiler 要启动 Profiler,在 Android Studio 中运行目标设备上的应用程序后,点击底部工具栏中的 **"Profiler"** 按钮即可打开 Profiler 界面。此时会自动连接到正在运行的应用实例并开始记录数据[^1]。 #### 实时查看内存使用情况 在 Profiler 主界面中切换至 **Memory** 面板,这里可以看到应用当前的内存占用趋势图以及详细的内存分配信息。该面板提供了以下几个主要功能按钮: - **Dump Java Heap**: 获取当前内存快照以便进步分析可能存在的对象泄露。 - **Force Garbage Collection**: 手动触发次垃圾回收操作以观察释放后的实际内存状态。 - **Record Memory Allocations**: 开始/停止追踪新创建的对象及其大小分布状况。 #### 进行深入分析 当怀疑存在某些特定类型的资源未被正确释放时(比如Bitmaps 或者数据库游标),可以通过上述提到的功能配合具体场景测试验证假设是否成立。例如定期执行GC后再对比前后差异;或者录制段时间内的所有分配动作之后查找异常增长模式等等。 ```java // 示例代码展示如何模拟可能导致内存泄漏的情况 public class MainActivity extends AppCompatActivity { private static Drawable drawable; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ImageView imageView = new ImageView(this); setContentView(imageView); // 将Context设置给静态变量可能会引起内存泄漏 drawable = getResources().getDrawable(R.drawable.large_bitmap); imageView.setImageDrawable(drawable); } } ``` 通过以上方法论结合实践案例学习掌握如何有效运用 Android Studio 内置 Profile 功能去诊断解决常见的移动开发难题是非常有必要的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值