乐趣区

关于android:Android-Studio-41-中的本地内存分析

本文是 Android Studio 4.1 中 Profiler 有哪些新增个性 的第二局部。之前的文章侧重于介绍 Android Studio 中 System Trace 的新增性能

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

在调试内存问题的时候,通常的做法是搞清楚什么在占用内存,什么在开释内存。本文接下来会带着大家一起应用 Native Memory Profiler 来发现内存透露,并且应用 GPU 模仿压力测试 (Gpu Emulation Stress Test) 作为示例工程。

筹备工作

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

当咱们发现可疑的内存透露时,最好的做法是从更高的档次开始并且察看零碎内存的图形。您只须要在 Android Studio 中点击 profile 按钮,而后关上内存分析器,外面会显示更加具体的内存追踪信息。

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

运行了几次模拟器后,咱们能够发现一些乏味的景象:

  1. 对于 GPU 模仿利用来说,GPU 内存减少是理所应当的,然而 Activity 被 finish 之后,该内存仿佛被清空了。
  2. 每当咱们关上 GPUEmulationStressTestActivity 的时候,本地内存都会有所增加,然而每次运行后该内存仿佛并没有被重置,这就表明可能存在内存透露。

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

从 Android Studio 4.1 Canary 6 开始,咱们能够通过获取本地内存调配记录来剖析为何内存未被开释。为了可能在 GPU 模仿利用上进行该项操作,我先进行正在运行的利用,而后启动剖析一个新的实例。从一个初始的状态开始,会有助于咱们放大须要关注的范畴,尤其是在钻研一套不相熟的代码的时候更是如此。通过内存分析器,我能够取得整个 GPU 模仿示例运行期间的本地内存调配记录。咱们须要点击 Run->Profile->‘app’ 来重启利用。利用启动后 profile 窗口会关上,点击内存分析器,而后抉择 record native allocation

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

有些游戏或者利用所依赖的库会在 new 关键字之外调用 malloc 来申请内存。这个表格视图突出显示了这种状况,因此在应答这类游戏或利用时十分有用。

当记录加载后,数据会以表格的模式出现。表格中会显示调用 malloc 的叶子函数。除了显示函数名,表格里还会蕴含模块、调用计数、空间大小、和 delta 值。这些信息会被进行采样,因而不是所有的 malloc 或 free 的调用都会被捕捉到。这很大水平上取决于采样率,前面咱们会探讨它。

另外很有必要理解这些占用内存的函数是被哪些函数调用的。有两种办法可图形化该信息。第一种办法是将 “Arrange by allocation method” 选项改为 “Arrange by call stack”。表格会显示调用栈的树结构,和 CPU 记录里的相似。如果以后我的项目蕴含符号 (通常实用于可调试构建,如果您正在剖析一个内部的 APK,能够参考一下 文档),他们会主动被选取并启用。这样您就能够通过右键点击函数并 “Jump to source” 来间接转向源码。

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

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

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

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

  • “ 调配容量 ” (Allocation Size) 属于默认值,示意被追踪的内存总量;
  • “ 调配计数 ” (Allocation Count) 示意分配内存的对象总数;
  • “ 全副残余容量 ” (Total Remaining Size) 示意在数据采集完结之前,整个数据采集过程中未被开释的内存容量;
  • “ 全副残余计数 ” (Total Remaining Count) 和残余容量相似,示意在采集完结之前,整个采集过程中未被开释的对象总数。

采集数据加载之后,在 “ 全副残余容量 ” 视图里,很容易发现 “lodepng” 所调配的内存容量比拟大

从这里咱们能够间接右键点击调用栈,而后抉择 “ 转向源码 ” (Jump to Source),而后会间接转向内存调配相干的源码。不过,咱们略微花些工夫看一下这里的可视化图形,会发现这里共享的父节点 WorldState 造成了多个透露问题。要验证这点,能够通过图形来过滤后果。

过滤 / 导航

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

有的时候调用栈会比拟长,或者仅仅因为屏幕的空间有余而无奈残缺显示全副函数的名称。您能够应用 ctrl 加鼠标滚轮进行缩放,或者能够点击图表,应用 W、A、S、D 进行导航。

验证后果

减少断点,并且疾速运行两次模拟器,而后发现第二次运行的时候,因为咱们笼罩了第一次运行时的一个指针造成了内存透露。

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

作为疾速解决方案,咱们能够在解决完结后开释掉 sWorld 变量,而后再次剖析利用来验证问题是否解决。

咱们还是察看高层次的内存统计。验证了在模仿运行完结的时候删除 sWorld 开释了最后运行时占用的 70 MB。

利用启动剖析和采样率设置

下面的例子展现了如何通过本地内存追踪来定位和解决内存透露问题。另一个本地内存追踪的常见用法是了解利用启动时内存的占用状况。在 Android Studio 4.1 中,咱们还减少了在利用启动时采集本地内存应用记录的性能。您能够在 “Run/Debug Configuration” 里的 “Profiling” 标签页进行设置。

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

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

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

总结一下

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

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

退出移动版