Memory Profiler 是 Android Profiler 中的一个组件,可帮助您识别可能会导致应用卡顿、冻结甚至崩溃的内存泄漏和内存抖动。它显示一个应用内存使用量的实时图表,让您可以捕获堆转储、强制执行垃圾回收以及跟踪内存分配。
要打开 Memory Profiler,请按以下步骤操作:
点击上图红框中 MEMORY区域 的任意位置,就会出现下图:
上图中,Memory Profiler 的默认视图包括以下各项:
①.用于强制执行垃圾回收事件的按钮。
②.用于捕获堆转储的按钮。
注意:只有在连接到搭载 Android 7.1(API 级别 25)或更低版本的设备时,才会 在堆转储按钮右侧显示用于记录内存分配的按钮。
③.一个下拉菜单,用于指定分析器捕获内存分配的频率。选择适当的选项可帮助您在分析时提高应用性能。
④.用于放大/缩小时间轴的按钮。
⑤.用于跳转到实时内存数据的按钮。
⑥.事件时间轴,显示活动状态、用户输入事件和屏幕旋转事件。
⑦.内存使用量时间轴,它包括以下内容:
不过,如果您使用的是搭载 Android 7.1 或更低版本的设备,则并非所有分析数据在默认情况下都可见。如果您看到一条消息,显示“Advanced profiling is unavailable for the selected process”,您需要启用高级分析才能看到以下内容:
在 Android 8.0 及更高版本上,始终为可调试应用启用高级分析
在 Memory Profiler 顶部看到的数字(下图)基于您的应用根据 Android 系统机制所提交的所有私有内存页面。此计数不包含与系统或其他应用共享的页面。
由于本示例是在6.0系统的手机上做的演示,图中Allocated后面未显示具体的数字,内存计数中的类别如下:
与以前的 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 引用是如何分配的(相当于Allocation Tracker工具,用来跟踪内存分配,它允许你在执行某些操作的同时监视在何处分配对象,)。具体而言,Memory Profiler 可为您显示有关对象分配的以下信息:
如果您的设备搭载的是 Android 8.0 或更高版本,您可以随时查看对象分配,具体操作步骤如下:在时间轴上拖动以选择要查看哪个区域的分配。不需要开始记录会话,因为 Android 8.0 及更高版本附带设备内置分析工具,可持续跟踪您的应用分配。
如果您的设备搭载的是 Android 7.1 或更低版本,请点击 Memory Profiler 工具栏中的 Record memory allocations 图标 。记录时,Memory Profiler 会跟踪您的应用中发生的所有分配。完成后,请点击 Stop recording 图标 (同一按钮)以查看分配。
如下图所示:
完成记录会话后(或者使用搭载 Android 8.0 或更高版本的设备完成选择时间轴的某个区域后),已分配对象的列表将显示在时间轴下方,按类名称进行分组,并按其堆计数排序。
注意:在 Android 7.1 及更低版本上,您最多可以记录 65535 个分配。如果您的记录会话超出此限制,则记录中仅保存最新的 65535 个分配。(在 Android 8.0 及更高版本上,则没有实际的限制。)
要检查分配记录,请按以下步骤操作:
1.浏览列表以查找堆计数异常大且可能存在泄漏的对象。为帮助查找已知类,点击 Class Name 列标题以按字母顺序排序。然后,点击一个类名称。此时右侧将出现 Instance View 窗格,显示该类的每个实例,如下图所示:
可以使用已分配对象列表上方的两个菜单来选择要检查的堆以及如何组织数据。
从左侧的菜单中,选择要检查的堆:
从右侧的菜单中,选择如何安排分配:
为了在分析时提高应用性能,Memory Profiler 在默认情况下会定期对内存分配进行采样。在运行 API 级别 26(8.0系统) 或更高级别的设备上进行测试时,您可以使用 Allocation Tracking 下拉菜单来更改此行为。可用选项如下:
注意:默认情况下,Android Studio 会在执行 CPU 记录时停止跟踪实时分配,并在 CPU 记录完成后重新开启该功能。您可以在 CPU 记录配置对话框中更改此行为。
Java 原生接口 (JNI) 是一个允许 Java 代码和原生代码相互调用的框架。
JNI 引用由原生代码进行管理,因此原生代码使用的 Java 对象可能会保持活动状态太长时间。如果丢弃了 JNI 引用而未先明确将其删除,Java 堆上的某些对象可能会变得无法访问。此外,还可能会达到全局 JNI 引用限制。
要排查此类问题,请使用 Memory Profiler 中的 JNI heap 视图来浏览所有全局 JNI 引用,并按 Java 类型和原生调用堆栈对其进行过滤。借助此信息,您可以了解创建和删除全局 JNI 引用的时间和位置。
在您的应用运行时,选择您要检查的一部分时间轴,然后从类列表上方的下拉菜单中选择 JNI heap。您随后可以像往常一样检查堆中的对象,还可以双击 Allocation Call Stack 标签中的对象,以查看在代码中将 JNI 引用分配和释放到了什么位置,如下图所示。
要检查应用的 JNI 代码的内存分配,您必须将应用部署到搭载 Android 8.0 或更高版本的设备上。
堆转储(Heap Dump 主要功能就是查看不同的数据类型在内存中的使用情况。它可以帮助你
找到大对象,也可以通过数据的变化发现内存泄漏。)显示在您捕获堆转储时您的应用中哪些对象正在使用内存。特别是在长时间的用户会话后,堆转储会显示您认为不应再位于内存中却仍在内存中的对象,从而帮助识别内存泄漏。
捕获堆转储后,您可以查看以下信息:
要捕获堆转储,请点击 Memory Profiler 工具栏中的 Dump Java heap 图标 。下图中红框中的图标就是Dump Java heap 图标
在捕获堆转储期间,Java 内存量可能会暂时增加。这很正常,因为堆转储与您的应用发生在同一进程中,并需要一些内存来收集数据。下图是点击了 Dump Java heap 图标 后捕获的堆转储
在类列表中,您可以查看以下信息:
Allocations:堆中的分配数。
Native Size:此对象类型使用的原生内存总量(以字节为单位)。只有在使用 Android 7.0 及更高版本时,才会看到此列。
您会在此处看到采用 Java 分配的某些对象的内存,因为 Android 对某些框架类(如 Bitmap)使用原生内存。
Shallow Size:此对象类型使用的 Java 内存总量(以字节为单位)。
Retained Size:为此类的所有实例而保留的内存总大小(以字节为单位)。
您可以使用已分配对象列表上方的两个菜单来选择要检查的堆转储以及如何组织数据。
从左侧的菜单中,选择要检查的堆:
从右侧的菜单中,选择如何安排分配:
默认情况下,此列表按 Retained Size 列排序。要按其他列中的值排序,请点击该列的标题。
点击一个类名称可在右侧打开 Instance View 窗口(如下图所示)。
列出的每个实例都包含以下信息:
注意:默认情况下,堆转储不会向您显示每个已分配对象的堆栈轨迹。要获取堆栈轨迹,在点击 Dump Java heap 之前,您必须先开始记录内存分配。然后,您可以在 Instance View 中选择一个实例,并查看 References 标签旁边的 Call Stack 标签,如上图所示。不过,在您开始记录分配之前,可能已分配一些对象,因此不会显示这些对象的调用堆栈。包含调用堆栈的实例在图标 上用一个“堆栈”标志表示。(遗憾的是,由于堆栈轨迹需要您执行分配记录,因此您目前无法在 Android 8.0 上查看堆转储的堆栈轨迹。)
要检查您的堆,请按以下步骤操作:
1.浏览列表以查找堆计数异常大且可能存在泄漏的对象。为帮助查找已知类,点击 Class Name 列标题以按字母顺序排序。然后,点击一个类名称。此时右侧将出现 Instance View 窗格,显示该类的每个实例,上图所示
2.在 Instance View 窗格中,点击一个实例。此时下方将出现 References 标签,显示对该对象的每个引用。或者,点击实例名称旁边的箭头以查看其所有字段,然后点击一个字段名称以查看其所有引用。如果您要查看某个字段的实例详细信息,请右键点击该字段并选择 Go to Instance。
3.在 References 标签中,如果您发现某个引用可能在泄漏内存,请右键点击它并选择 Go to Instance。这样会从堆转储中选择相应的实例,从而向您显示它自己的实例数据。
在您的堆转储中,请注意由下列任意情况引起的内存泄漏:
捕获堆转储后,只有在 Memory Profiler 正在运行时,才能在该分析器中查看数据。当您退出分析会话时,会丢失堆转储。因此,如果您要保存堆转储以供日后查看,请将其导出到 HPROF 文件。在 Android Studio 3.1 及更低版本中,Export capture to file 按钮 位于时间轴下方工具栏的左侧;在 Android Studio 3.2 及更高版本中,Sessions 窗格中每个 Heap Dump 条目的右侧都有一个 Export Heap Dump 按钮。
点击上图红框中的鼠标处的Export Heap Dump 按钮,在随即显示的 Export As 对话框中,使用 .hprof 文件扩展名保存文件。
要使用其他 HPROF 分析器(如 jhat),您需要将 HPROF 文件从 Android 格式转换为 Java SE HPROF 格式。您可以使用 android_sdk/platform-tools/ 目录中提供的 hprof-conv 工具执行此操作。运行包含两个参数(即原始 HPROF 文件和转换后 HPROF 文件的写入位置)的 hprof-conv 命令。例如:
hprof-conv heap-original.hprof heap-converted.hprof
要导入一个 HPROF (.hprof) 文件,请点击 Sessions 窗格中的 Start a new profiling session 图标 ,选择 Load from file,然后从文件浏览器中选择该文件。可以参考上面的第二张图,找到 Load from file 选项。
您还可以通过将 HPROF 文件从文件浏览器拖动到编辑器窗口来导入该文件。
使用 Memory Profiler 时,您应对应用代码施加压力并尝试强制内存泄漏。在应用中引发内存泄漏的一种方式是,先让其运行一段时间,然后再检查堆。泄漏在堆中可能逐渐汇聚到分配顶部。不过,泄漏越小,为了看到泄漏而需要运行应用的时间就越长。
您还可以通过以下某种方式来触发内存泄漏:
参考:
使用 Memory Profiler 查看 Java 堆和内存分配