Android Studio 4.1 中的本地内存分析

本文是 Android Studio 4.1 中 Profiler 有哪些新增特性 的第二部分。以前的文章侧重于介绍 Android Studio 中 System Trace 的新增功能android

咱们从你们的反馈了解到使用 C++ 调试本地内存很是困难,尤为在开发游戏的时候。在 Android Studio 4.1 中,Memory Profiler (内存分析器) 能够记录本地内存分配的调用栈。本地内存记录基于 Perfetto 后端实现,它是 Android 的新一代性能工具和问题追溯解决方案。git

在调试内存问题的时候,一般的作法是搞清楚什么在占用内存,什么在释放内存。本文接下来会带着你们一块儿使用 Native Memory Profiler 来发现内存泄漏,而且使用 GPU 模拟压力测试 (Gpu Emulation Stress Test) 做为示例工程。github

准备工做

首先,咱们从 https://github.com/google/gpu-emulation-stress-test 克隆或者下载源码。正则表达式

当咱们发现可疑的内存泄漏时,最好的作法是从更高的层次开始而且观察系统内存的图形。您只须要在 Android Studio 中点击 profile 按钮,而后打开内存分析器,里面会显示更加详细的内存追踪信息。后端

内存分析器的顶层视图,从显示中能够看到每次运行 "GPU emulation stress test" 的时候内存占用都会逐步升高

内存分析器的顶层视图,从显示中能够看到每次运行 "GPU emulation stress test" 的时候内存占用都会逐步升高

运行了几回模拟器后,咱们能够发现一些有趣的现象:app

  1. 对于 GPU 模拟应用来讲,GPU 内存增长是理所应当的,然而 Activity 被 finish 以后,该内存彷佛被清空了。
  2. 每当咱们打开 GPUEmulationStressTestActivity 的时候,本地内存都会有所增长,可是每次运行后该内存彷佛并无被重置,这就代表可能存在内存泄漏。

Native Memory Table (本地内存表格) 视图

从 Android Studio 4.1 Canary 6 开始,咱们能够经过获取本地内存分配记录来分析为什么内存未被释放。为了可以在 GPU 模拟应用上进行该项操做,我先中止正在运行的应用,而后启动分析一个新的实例。从一个初始的状态开始,会有助于咱们缩小须要关注的范围,尤为是在研究一套不熟悉的代码的时候更是如此。经过内存分析器,我能够得到整个 GPU 模拟示例运行期间的本地内存分配记录。咱们须要点击 Run->Profile-> ‘app’ 来重启应用。应用启动后 profile 窗口会打开,点击内存分析器,而后选择 record native allocationide

本地内存记录在 Android Studio 中加载时的初始状态

本地内存记录在 Android Studio 中加载时的初始状态

有些游戏或者应用所依赖的库会在 new 关键字以外调用 malloc 来申请内存。这个表格视图突出显示了这种状况,于是在应对这类游戏或应用时很是有用。函数

当记录加载后,数据会以表格的形式呈现。表格中会显示调用 malloc 的叶子函数。除了显示函数名,表格里还会包含模块、调用计数、空间大小、和 delta 值。这些信息会被进行采样,所以不是全部的 malloc 或 free 的调用都会被捕捉到。这很大程度上取决于采样率,后面咱们会讨论它。工具

另外颇有必要了解这些占用内存的函数是被哪些函数调用的。有两种方法可图形化该信息。第一种方法是将 "Arrange by allocation method" 选项改成 "Arrange by call stack"。表格会显示调用栈的树结构,和 CPU 记录里的相似。若是当前项目包含符号 (一般适用于可调试构建,若是您正在分析一个外部的 APK,能够参考一下 文档),他们会自动被选取并启用。这样您就能够经过右键点击函数并 "Jump to source" 来直接转向源码。性能

在表格里右键点击一个元素会显示 "Jump to Source" 菜单

在表格里右键点击一个元素会显示 "Jump to Source" 菜单

内存可视化 (本地和非本地)

咱们还在内存分析器中增长了用于可视化数据的火焰图,您能够很是快速地找到分配内存最多的调用栈。该方法对于很深的调用栈很是有用。

有四种方式能够在 X 轴上对数据进行排序:

  • "分配容量" (Allocation Size) 属于默认值,表示被追踪的内存总量;
  • "分配计数" (Allocation Count) 表示分配内存的对象总数;
  • "所有剩余容量" (Total Remaining Size) 表示在数据采集结束以前,整个数据采集过程当中未被释放的内存容量;
  • "所有剩余计数" (Total Remaining Count) 和剩余容量相似,表示在采集结束以前,整个采集过程当中未被释放的对象总数。

采集数据加载以后,在 "所有剩余容量" 视图里,很容易发现 "lodepng" 所分配的内存容量比较大

采集数据加载以后,在 "所有剩余容量" 视图里,很容易发现 "lodepng" 所分配的内存容量比较大

从这里咱们能够直接右键点击调用栈,而后选择 "转向源码" (Jump to Source),而后会直接转向内存分配相关的源码。不过,咱们稍微花些时间看一下这里的可视化图形,会发现这里共享的父节点 WorldState 形成了多个泄漏问题。要验证这点,能够经过图形来过滤结果。

过滤/导航

和表格视图相似,图表能够经过过滤栏 (filter bar) 进行数据过滤。当启用过滤的时候,图表的数据会自动进行更新,仅显示函数符合关键词或者正则表达式的调用栈。

有的时候调用栈会比较长,或者仅仅由于屏幕的空间不足而没法完整显示所有函数的名称。您可使用 ctrl 加鼠标滚轮进行缩放,或者能够点击图表,使用 W、A、S、D 进行导航。

验证结果

增长断点,而且快速运行两次模拟器,而后发现第二次运行的时候,因为咱们覆盖了第一次运行时的一个指针形成了内存泄漏。

调试器的 Quick 视图显示第二次运行的时候 "sWorld" 已经有值了

调试器的 Quick 视图显示第二次运行的时候 "sWorld" 已经有值了

做为快速解决方案,咱们能够在处理结束后释放掉 sWorld 变量,而后再次分析应用来验证问题是否解决。

咱们仍是观察高层次的内存统计。验证了在模拟运行结束的时候删除 sWorld 释放了最初运行时占用的 70 MB。

应用启动分析和采样率设置

上面的例子展现了如何经过本地内存追踪来定位和解决内存泄漏问题。另外一个本地内存追踪的常见用法是理解应用启动时内存的占用状况。在 Android Studio 4.1 中,咱们还增长了在应用启动时采集本地内存使用记录的功能。您能够在 "Run/Debug Configuration" 里的 "Profiling" 标签页进行设置。

Profiling 标签页位于 Run Configuration 对话框中

Profiling 标签页位于 Run Configuration 对话框中

您能够在 Run 配置对话框中自定义采集间隔或者设置应用启动时记录内存使用状况。

这里您还能够为新的采集修改采样率。更小的采样率会对整个性能产生很大的影响,而更大的采样率则会遗漏一些内存分配记录。不一样的采样率针对不一样类型的内存问题。

总结一下

经过全新的本地内存分析器能够定位内存泄漏而且轻松洞悉内存使用状况。快去 Android Studio 4.1 试试本地内存分析器吧。若是有任何问题和反馈能够 给咱们留言。更多小窍门能够查阅咱们今年早些时候在 Google 游戏峰会分享的内容:

https://www.bilibili.com/vide...

相关文章
相关标签/搜索