捕获堆转储

捕获堆转储可查看应用中的哪些对象在捕获时占用了内存,并识别内存泄漏或导致卡顿、冻结甚至应用崩溃的内存分配行为。在长时间的用户会话后,堆转储会显示您认为不应再位于内存中却仍在内存中的对象,因此特别有用。

本页介绍了 Android Studio 提供的用于收集和分析堆转储的工具。或者,您也可以使用 dumpsys 从命令行检查应用内存,并在 Logcat 中查看垃圾回收 (GC) 事件。

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

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

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

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

堆转储概览

如需捕获堆转储,请选择 分析内存用量 (堆转储) 任务(使用 性能分析器: 以可调试方式运行“应用”(完整数据))来捕获堆转储。在转储堆期间,Java 内存量可能会暂时增加。这很正常,因为堆转储与您的应用发生在同一进程中,并需要一些内存以收集数据。捕获堆转储后,您会看到以下内容:

Android Studio 性能分析器中的堆转储视图。

课程列表会显示以下信息:

  • Allocations:堆中的分配数。
  • Native Size:此对象类型使用的原生内存总量(以字节为单位)。您会在此处看到采用 Java 分配的某些对象的内存,因为 Android 对某些框架类(如 Bitmap)使用原生内存。

  • 浅层大小:此对象类型使用的 Java 内存总量(以字节为单位)。

  • 保留大小:为此类的所有实例而保留的内存总大小(以字节为单位)。

使用堆菜单过滤到特定堆:

  • 应用堆(默认):应用在其中分配内存的主堆。
  • 映像堆:系统启动映像,包含启动期间预加载的类。此处的分配永远不会移动或消失。
  • Zygote 堆:Android 系统中应用进程派生自的写时复制堆。

使用排列下拉菜单选择如何排列分配:

  • 按类排列(默认):根据类名称对所有分配进行分组。
  • Arrange by package:根据软件包名称对所有分配进行分组。

使用类别下拉菜单可过滤到类别组:

  • 所有类(默认):显示所有类,包括来自库和依赖项的类。
  • 显示 activity/fragment 泄漏:显示导致内存泄漏的类。
  • Show project classes:仅显示由您的项目定义的类。

点击类名称以打开 Instance 窗格。列出的每个实例都包含以下内容:

  • Depth:从任意 GC 根到选定实例的最短跳数。
  • Native Size:此实例在原生内存中的大小。此列仅对 Android 7.0 及更高版本可见。
  • Shallow Size:Java 内存中此实例的大小。
  • 保留大小:此实例支配的内存大小(根据支配树)。

点击某个实例可显示实例详情,包括其字段和引用。常见的字段和引用类型包括 Java 中的结构化类型 、数组 和基元数据类型 。右键点击字段或引用可转到源代码中的关联实例或代码行。

  • 字段:显示此实例中的所有字段。
  • 引用:显示对实例标签页中突出显示的对象的所有引用。
Heap Dump 工具窗口中的 Instances、Fields 和 References 视图。

检测重复的位图

从 Android Studio Narwhal 4 开始,您还可以在“堆转储”视图中检测冗余位图。

以下是查找这些应用的方法:

  1. 在 Android Studio 中打开“Profiler”标签页
  2. 点击 堆转储(或 内存用量分析),然后点击“Record”以截取应用当前内存状态的快照。
  3. 在分析结果中查找黄色警告三角形 ⚠️,Android Studio 会使用该三角形标记多次存储的重复位图。
    • 或者,前往性能分析器标题,选择过滤条件:,然后选择重复的位图设置。
  4. 点击任何标记的条目即可打开位图预览窗格,以便您确切了解哪张图片是重复出现的违规图片。
  5. 利用该视觉确认,在代码中找到多余的加载逻辑,并实现更好的缓存策略。
使用黄色警告三角形 ⚠️ 查找重复的位图。

查找内存泄漏

如需快速过滤到可能与内存泄漏相关的类,请打开类下拉菜单,然后选择 Show activity/fragment leaks。Android Studio 会显示它认为可能表明应用中的 Activity 和 Fragment 实例存在内存泄漏的类。

如需更手动地查找内存泄漏,请浏览类和实例列表,查找保留大小较大的对象。查找由以下任何原因导致的内存泄漏:

  • 对 Activity 或 Context 的长期引用,可能会导致托管的 Compose 组合图(例如 ComposeView 及其子可组合项)发生泄漏。
  • 泄露 Jetpack Compose 状态对象 (MutableState)、状态容器或捕获 Context 的 lambda。
  • 忘记在 DisposableEffect 的 onDispose 块中清理监听器或观察器。
  • 非静态内部类(例如 Runnable),可以保留 Activity 实例。
  • 对象保持时间比所需时间长的缓存。

发现潜在的内存泄漏后,您可以使用 Instance Details 中的 Fields 和 References 标签页跳转到您感兴趣的实例或源代码行。

触发内存泄漏以进行测试

如需分析内存用量,您应该对应用代码进行压力测试,并尝试强制造成内存泄漏。在应用中引发内存泄漏的一种方法是让应用运行一段时间,然后再检查堆。泄漏可能会逐渐累积到堆中分配的顶部。不过,泄漏越小,您需要运行应用的时间就越长,这样才能看到泄漏。

您还可以通过以下某种方式触发内存泄漏:

  • 在不同的 activity 状态下,先将设备从竖屏旋转为横屏,再将其旋转回来,这样反复旋转多次。如果应用在异步操作或状态容器中保留对 Activity 或 Context 的引用,旋转设备通常会导致应用泄露 Activity(以及其托管的 Compose 界面树和关联的状态树)。
  • 在不同的 activity 状态下,在您的应用与其他应用之间切换。 例如,前往主屏幕,然后返回到您的应用。

导出和导入堆转储录制内容

您可以从性能分析器中的 Past Recordings 标签页导出和导入堆转储文件。Android Studio 会将录制内容保存为 .hprof 文件。

或者,如需使用其他 .hprof 文件分析器(如 jhat),您需要将 .hprof 文件从 Android 格式转换为 Java SE .hprof 文件格式。如需转换文件格式,请使用 {android_sdk}/platform-tools/ 目录中提供的 hprof-conv 工具。运行 hprof-conv 命令,并提供两个参数:原始 .hprof 文件名和写入转换后的 .hprof 文件的位置(包括新的 .hprof 文件名)。例如:

hprof-conv heap-original.hprof heap-converted.hprof

其他资源