写点什么

如何使用 DevEco Studio 性能调优工具 Profiler 定位应用内存问题

  • 2025-01-20
    广东
  • 本文字数:6911 字

    阅读完需:约 23 分钟

鸿蒙应用开发过程中,可能由于种种原因导致应用内存未被正的使用或者归还至操作系统,从而引发内存异常占用、内存泄漏等问题,最终导致应用卡顿甚至崩溃,严重影响用户体验。

DevEco Profiler 是集成在 DevEco Studio 中的一款原生鸿蒙应用性能优化工具,能够辅助开发者高效完成鸿蒙应用的性能问题定位与优化。在集成开发环境 DevEco Studio 中可以以如下方式打开 DevEco Profiler:

·   在 DevEco Studio 顶部菜单栏中选择“View -> Tool Windows -> Profiler”。

·   在 DevEco Studio 底部工具栏中单击“Profiler”。

·   按“Double Shift”或者“Ctrl+Shift+A”打开搜索功能,搜索“Profiler”。

DevEco Profiler 中提供了针对鸿蒙应用内存问题的场景化分析模板 Snapshot Insight 与 Allocation Insight,可以用于分析 ArkTS 以及 Native 内存,帮助开发者高效定位解决内存问题。下面将从识别问题、定界定位、优化验证三个方面来对 DevEco Profiler 定位内存问题的方法进行介绍。

1. 识别内存问题

1.1 监控应用内存

当应用的某项功能开发完成时,可以使用 DevEco Profiler 的实时监控功能(详细使用方法见性能问题定界:实时监控-使用DevEco Profiler进行性能调优-性能分析-DevEco Studio - 华为HarmonyOS开发者)对应用的各项资源进行监控,其中就包括应用内存资源。实时监控界面所展示的是应用在运行过程中实际所使用的物理内存(Proportional Set Size, PSS)、其他进程的物理内存占用以及操作系统的空闲内存,泳道的蓝色部分展示了当前进程的物理内存占用情况及趋势,左侧饼图则展示了当前时刻的瞬时内存使用数据。



此时,我们可以正常地操作应用,观察在当前功能运行过程中的应用内存变化情况,初步判断是否存在内存问题。当发现在一段时间内应用内存没有明显增加或者在内存上涨后又逐渐回落至正常水平,则基本可以排除应用存在内存问题;反之,如果应用内存占用明显与预期值不符合、在一段时间内不断上涨且无回落或者内存占用明显增长超出预期,那么则可初步判断应用可能存在内存问题,需要进一步分析。

1.2 初步定界内存问题

当从实时监控页面初步判断应用可能存在内存问题的时候,进一步地可以使用 Allocation Insight 或 Snapshot Insight 的 Memory 泳道来抓取应用内存在问题场景下的详细数据以及变化趋势,初步定界问题出现的位置(Native Heap/ArkTS Heap/dev 段等)。

在当前步骤下,录制内存数据时需要将 Allocation Insight 或 Snapshot Insight 中的其余泳道去除勾选,仅录制 Memory 泳道的数据(注:因为其余泳道会开启对内存分配、内存对象等数据的抓取,这些功能会带来额外的开销,可能会对我们初步定界问题产生噪音,影响分析,故先排除录制)。



在 Memory 泳道的录制过程中,不断操作应用在问题场景的功能,将问题放大,便于快速定界问题点,参考录制过程中的应用内存占用曲线,当曲线的上涨幅度达到一定大小时即可结束录制。

录制完成后,可以展开 Memory 泳道,查看应用内存分段的使用情况,也可以点击泳道上的 options 下拉框,自行选择想要关注变化情况的内存类型。



同时,可以选中 Memory 泳道或其任一子泳道(直接选中泳道时详情区域会展示完整的泳道数据)来查看在每个采样点的详细应用内存占用数据 (注:详情区域数据采用 PSS 的维度衡量,数据近似于使用`hidumper --mem $pid`的第一列 PSS 值)。当鼠标左键单击选中表格中某一行的数据时,对应的泳道上将展示出当前的时间刻度线,方便快速定位内存变化的时间位置。



通过查看 Memory 的子泳道内存分类以及详情区域的内存详细占用,我们能够大致定界出有哪些位置的内存可能存在问题。例如上图中从 Memory 的子泳道数据图中可以看到,FilePage Other/Native Heap/ArkTS Heap 均有较大的增长,因而可以以这三个方面的内存使用作为切入点,来进一步分析问题的根因。

2. 定位内存问题

从 1.2 节中的分析可知,当前应用内存的增长主要集中在 FilePage Other/Native Heap/ArkTS Heap 这三个部分,那么需要使用进一步的分析方法对这三个方面的内存进行分析,定位内存上涨的根因。

在分析鸿蒙应用的内存问题时,可以将鸿蒙应用的内存大体分为两部分,方舟虚拟机内存和 Native 内存:

1. 方舟虚拟机内存:由方舟虚拟机管控的应用内存,同其他的虚拟机内存(例如 Java)管理策略相似,开发者可以使用并操控的内存基本集中于虚拟机堆上,在方舟虚拟机上被称作 ArkTS Heap,这部分内存受到方舟虚拟机的管控。

2. Native 内存:这部分内存主要是应用使用到的一些涉及 Native 的 API 所申请的内存以及开发者自己的 Native 代码所申请使用的堆内存(通常是 C/C++),这部分内存需要开发者自己去管理申请和释放。

因为两种内存的使用方式和管理方式不尽相同,因此在对这两类内存的分析过程中所使用的方法也有比较大的区别,下面将从这两个方面分别介绍分析方法。

 

2.1 ArkTS 内存问题

2.1.1 ArkTS 内存管理

首先针对 ArkTS Heap,由于该部分堆内存受到方舟虚拟机的管理,可以对堆内存进行垃圾回收(Garbage Collect,GC)和拍摄快照(HeapSnapshot 或 HeapDump,简称 dump)以反映出瞬时的全量堆内存使用及分布情况,因此这部分内存的问题分析手段主要是对堆 dump 进行引用关系分析,分析泄漏对象无法被 GC 回收的原因。

不同于引用计数算法,方舟虚拟机采用可达性分析的机制来管理对象是否可被垃圾收集器回收,因此针对方舟虚拟机内存的分析方法主要集中于对象的引用分析,即分析哪些对象引用关系是错误的或者异常的,从而导致了泄漏对象被长时间持有无法被 GC,最终通过解除强引用关系的手段来解决内存泄露问题。如下图 1 所示,蓝色的对象节点表示在内存引用分析中该对象 GC Root 引用可达,其余对象 GC Root 引用不可达,引用不可达的对象在 GC 中可以被虚拟机回收。



图 1 对象可达性分析

2.1.2 ArkTS Heap 分析

针对虚拟机的内存问题分析通常都集中在对 dump 的对象分布及引用关系的分析上,方舟虚拟机也不例外,这里在 DevEco Profiler 上提供了 Snapshot Insight 来对方舟虚拟机的堆内存进行分析。



在使用 Snapshot 分析时,通常会使用三快照技术(Three Snapshot Technique),通过内存快照的对比视图将某两次快照之间分配且仍然驻留的内存筛选出来,这些对象中的一部分就可能是导致内存泄漏的对象。通用的流程为:

打开应用,初始化场景 (触发 GC)-> 拍摄第一次 Snapshot 作为基准 -> 多(N)次触发内存泄漏操作 -> 拍摄第二次堆快照 -> 触发主动 GC -> 拍摄第三次堆快照

由于方舟虚拟机提供了在获取堆快照之前自动 GC 的功能,因此我们可以将上述流程简化为两步,同时加上 Profiler 的录制功能,整体流程为:

打开应用,初始化场景 -> 开启录制 Snapshot Insight-> 拍摄第一次 Snapshot 作为基准 -> 多(N)次触发内存泄漏操作 -> 拍摄第二次堆快照 -> 结束录制

录制完成后,会得到如下图所示的数据



录制过程中,我们采集了两次堆快照,对应在 Profiler 的界面上就是两个紫色的条块,每一个条块内的数据都是当前的虚拟机堆快照。条块上的数字大小代表的是虚拟机堆内存的实际占用。

由于在每次拍摄堆快照之前,虚拟机都会触发 GC,所以理论上堆快照内存在的对象都是当前虚拟机已经无法 GC 掉的对象,所以我们可以将两个堆快照进行比较,来查看哪些对象是我们在触发问题场景时新增了且不能释放的。

点击 Snapshot Insight 面板的 Comparison 页签,将两次 Snapshot 进行比较,如下图。图中数据的含义为以 Snapshot2 作为基准,Snapshot2 对比 Snapshot1 的数据变化量。



即便是比较视图,东西也非常多,怎么分析呢?

还记得上面说的操作 N 次吗,在触发内存问题场景时将问题触发 N 次,在比较视图中首先就去找与 N 强相关、与业务代码强相关的 constructor,首先来分析这些对象是否正常。

首先介绍一下 Snapshot 比较视图中各项数据的含义,如下图,更加具体的也可以参考内存泄露分析:Snapshot分析-性能分析-DevEco Studio - 华为HarmonyOS开发者或者使用Snapshot Insight分析ArkTS内存问题-华为开发者问答专区 | 华为开发者联盟 (huawei.com)



在找到相关的业务关联的对象后,可以从 references 里面一层层去寻找、排查在引用链上的可疑对象(一般指与业务代码关联的对象,尤其是 xxx in com.xxx.yyy 这种明显是业务使用到的对象的位置)。

在排查时,可以主要往两个方向:

1)Distance 逐渐减小;

2)引用链上都是业务相关的对象。

从这两个维度去分析入度引用链,找出那些业务逻辑上应该释放但是实际并没有释放的对象及其引用关系,从这些引用关系上来排查是否有不合理的强引用导致的内存无法释放的问题,进而解决内存泄漏问题。

具体的案例可以参考:使用Snapshot Insight分析ArkTS内存问题-华为开发者问答专区 | 华为开发者联盟 (huawei.com)

2.2 分析 Native 内存

其次针对 Native Heap,由于该部分堆内存仅由开发者自行控制分配释放,无法使用类似虚拟机的 dump 手段来分析整体的堆内存使用情况,仅可使用 Profiler 抓取到录制过程中的应用内存分配和释放事件,因此需要开发者通过内存分配/释放事件以及内存分配栈等信息自行找到代码中的内存申请和释放点来确认是否存在申请、释放不配对导致的内存泄漏问题。

内存分析模板 Allocation 提供了该能力,使用该模板可以抓取到经由系统基础库分配的 Native 部分内存,分析分配/释放的匹配逻辑,界面如下图。



在开始录制前,需要先了解一些该模板的工作原理以及相关的配置参数,以确定能够抓取到应用中可能存在的内存问题。

Allocation 模板通过 hook 基础库中的某些函数调用来获取每次内存分配的数据,并将这些数据返回至 Profiler 中,在 Profiler 中完整的展示这些内存的分配、释放数据以及相关的调用栈、库等信息。详细的使用介绍可以参考这里内存分析及优化-基础内存分析:Allocation分析-性能分析-DevEco Studio - 华为HarmonyOS开发者,下面就具体的使用流程做介绍。

 

2.2.1 Native 数据采集

首先,是录制前的参数配置,最新版本的配置页面如下,其中的配置项都是针对 Native Allocation 这条泳道的,下面依次介绍:

1)Statistics Mode、Sampling Interval:该项配置代表是否开启统计模式采集数据,默认开启。开启后,数据会每隔 Sampling Interval 中设置的时间从设备端汇总并返回,该模式下工具的采集性能会更好、负载更低、可采集的时间也更长,但是会丢失掉精准的每次分配释放的内存信息;关闭该模式,会开启详情采集模式,返回的数据中会给出每次内存分配的详细情况,包含内存分配地址、分配大小、分配栈等信息。推荐使用统计模式,如业务侧有需要也可酌情使用详情模式。

2)Filter Size:过滤内存大小。该参数表示最小抓取的内存大小,默认为 1024bytes(1kb),内存分配时小于该大小的内存分配信息不会被抓取到,应用可根据自身具体情况选择该参数。该参数可能会显著影响应用性能,当该值过小且应用在分配大量内存的场景时(抓取的内存分配数据量巨大)可能会造成应用卡顿,因此建议选择合适的参数。

3)Backtrace Mode:内存分配栈回栈方式,默认 FP 回栈。FP 回栈性能更好,默认开启,但在某些特定场景下(例如 so 的编译参数控制),FP 回栈可能失效,此时可选择 DWARF 回栈尝试。

4)Record JS Stack:是否开启 JS 回栈。当该开关打开时,系统回栈时会自动从 Native 向 JS 层回栈,完成 Native 到 JS 的栈缝合,适合 ArkTS/JS 代码调用 Native 的场景。

5)JS Backtrace Depth、Native Backtrace Depth:内存回栈深度。代表最大的回栈层数,层数越大对性能开销越大,可结合业务动态调整。

注:当选择了 DWARF 回栈后,JS 和 Native 的回栈深度会变成一个框 Backtrace Stack,其层数代表着 JS 与 Native 的共同回栈深度。



做好配置后,可以开始抓取问题场景的数据,切记需要在保存数据并开启录制之后,操作应用复现问题场景,并在问题复现完成后停止录制。(注:如果是使用统计模式,录制的结束时间需要是 Sampling Interval 即采集周期的整数倍,例如当采集周期是 10s 时,停止时间建议在 11s+/21s+/31s+,以此类推,留出余量给系统做数据处理与传输)。录制完成后界面类似下图。



其中泳道部分展示了当前的内存变化情况,Native Allocation 泳道下方又涉及两个子泳道 All Heap 和 All Anonymous VM,这两个泳道分别代表使用 malloc 和 mmap 函数分配的内存情况,下方的详情区域展示对应泳道的内存分配统计数据(Statistics 页签)与内存分配栈(Call Trees)信息。

 

2.2.2 Native 数据分析

抓取到了问题场景的数据,接下来就是对问题数据的分析。

首先,框选范围内的数据展示的是:在框选范围的起点之后及在框选范围的终点之前的所有内存分配的数据,这个逻辑很重要,会对分析结论有很大影响,需重点关注。

接下来,在详情面板的左下角有一个下拉单选框,能够筛选当前详情区域展示的内存的数据,如下图所示:

1)All Allocations:框选的时间段的所有分配内存信息。

2)Created & Existing:在框选范围的起点之后分配的,且在框选范围的终点之前没有释放的内存数据。

3)Created & Released:在框选范围的起点之后分配的,且在框选范围的终点之前已经释放的内存数据。

这三个选项可以根据具体的业务问题和诉求来确定。



第二个框中的 Native Size 和 Native Library 代表可以通过这两个维度:大小和分配库的信息来做统计,一个是按照分配大小聚合、另一个是按照分配该内存的库来聚合。



通过这个页签的统计信息、对未释放的数量、大小等列排序后,能够大致分析出内存出现问题的情况以及场景,为分析内存栈做一些提前准备。接下来可以把数据切换到 Call Trees 内存分配栈上。



该部分数据展示了详细的内存分配栈信息,内存问题一般都是在该部分通过栈帧信息找出相关的业务逻辑来定位。其中有两个点需要说明:

1)栈帧中主要为 Native 栈,除了应用本身编译的一些 so 及带有部分接口信息的 so 信息外,其他系统库部分仅展示 so 库与函数偏移信息,若需要查看这部分信息,需要导入相应版本的带符号的 so 库。(具体参考基础耗时分析:Time分析-性能分析-DevEco Studio - 华为HarmonyOS开发者中的离线符号解析小节)

2)开启 Record JS Stack 之后,部分栈帧中可能包含 JS 栈信息,JS 栈信息一般对应着应用源码,双击能够跳转到代码行上。

注:所有 Category 上有高亮色的栈帧信息,都可以通过双击跳转到源代码所在的文件和代码行上(前提是打开的工程是调优应用的相匹配的工程)。



同样地,在分析调用栈的过程中,可以使用下方操作栏上的一系列功能,除了上述介绍过的 All Allocations 筛选能力之外,在 Native Allocation 泳道的 Call Trees 页签中,可以通过底部的“Call Trees”和“Constraints”选择框来过筛选和过滤内存分配栈。



Call Trees 选择框包含两种过滤条件:

·   Separate by Allocated Size:在内存分配栈完全相同的情况下,会按照每次分配栈申请的内存大小将栈分开;

·   Hide System Libraries:隐藏内存分配栈中的系统堆栈。



Constraints 选择框也包含了两种过滤条件:

·   Count:根据指定的内存申请次数过滤内存分配栈信息;

·   Bytes:根据指定的内存申请大小过滤内存分配栈信息。

业务方可以根据自身的业务情况与具体的问题场景来适当使用这些数据筛选能力,同时在下方也提供搜索功能,Involves Symbol Name 搜索框提供了针对函数调用栈的文本搜索能力,可以快速搜索栈帧信息与 so 库信息并快捷跳转至对应位置。

 

另外,若表格的树状图不便于直观看出内存信息,可以使用 Flame Chart 火焰图开关,使用火焰图的形式来分析内存分配可能存在的问题,如下图,火焰图中的条块长度越长,代表该调用栈分配的内存大小越大。



2.2.3 Native 内存问题分析

在前面两个小节介绍了整个 Native Allocation 的使用及分析方式,这里简单介绍具体的问题分析逻辑:

1. 录制问题场景的内存分配信息;

2. 从统计信息及内存分配栈查看在当前范围内未释放的内存信息(选择 Created & Existing),通过 2.2.2 节中介绍的各种分析和筛选能力找出未释放的内存堆栈,并将该堆栈结合业务逻辑分析;

3. 若涉及 ArkTS 代码对 Native 的调用逻辑,可开启 Record JS Stack 开关,将 Native 的内存栈回栈至 JS 层,简化分析难度;

4. 通过这些问题栈帧信息映射到业务代码中,结合问题场景和代码分析为何该部分内存未释放,找到问题点。

 

3. 修改及验证

当经过上述步骤的流程分析完应用的内存问题之后,基本上已经可以定位到问题发生的位置及相关的代码段,在此基础上结合业务逻辑对代码做修改,修改后重新编译推包到真机上,在相同的场景下尝试复现问题,并使用实时监控或者 Allocation/Snapshot 模板的 Memory 泳道来监测应用内存占用情况,以确认问题是否还存在。



4. 总结

问题不是一朝一夕出现的,通常问题会在开发的过程中逐渐积累,到最终暴露出来时可能已经涉及了多个模块、多种逻辑,各种逻辑互相耦合,导致分析的难度大大增加。

这种情况下,我们建议把性能相关的工作也能做到平时,在开发过程中也去关心程序的性能问题。例如,刚写了一段很长的引用关系、增加了一些注册实例的逻辑或者做了一些组件间的变量传递,这种时候就可以去结合逻辑自己设想一下,会不会引发一定的性能问题,甚至可以在平时就用调优工具来检测一把。

这样做到每个开发阶段都保证了性能的可靠,那么在项目日益增大的同时,性能问题也不会严重到离谱、无法分析。

 

5. 附录

DevEco Studio 下载链接:DevEco Studio -华为开发者联盟

DevEco Profiler 官方指导文档:性能分析-DevEco Studio - 华为HarmonyOS开发者

用户头像

每一位开发者都是华为要汇聚的星星之火 2021-10-15 加入

提供HarmonyOS关键技术解析、版本更新、开发者实践和活动资讯,欢迎各位开发者加入HarmonyOS生态,一起创造无限可能!

评论

发布
暂无评论
如何使用DevEco Studio性能调优工具Profiler定位应用内存问题_DevEco Studio_HarmonyOS开发者_InfoQ写作社区