网页 综合 更新时间 2026-11-14 01:28:07

使用mem-analyzer高效分析ArkTS内存泄漏 - InfoQ

自动蜘蛛 www.infoq.cn 正文约 4422 字
使用mem-analyzer高效分析ArkTS内存泄漏 - InfoQ
ArkTS中ArkUI组件泄漏可通过快照日志自动识别,依赖框架在DISAPPERED状态后标记isDeleting_字段,检测准确率高且无需开发者适配。
1)泄漏引用链明确,修复成本低;2)快照日志支持命令行/IDE/API多途径获取;3)修复可消除底层native内存分析干扰。
适合ArkTS开发者、内存问题排查工程师、鸿蒙平台性能优化工程师阅读。
在 ArkTS 内存泄漏中,有一类是 ArkUI 组件泄漏,只需要有快照日志就能完成对此问题的分析,不依赖开发者做任何适配。其利用 ArkUI 框架自动标记已销毁组件,分析准确率很高。当前 ArkUI 组件相关泄漏非常普遍,建议在日常开发过程中对此类问题应改尽改,原因是:1)此类内存泄漏引用链明确,修改简单;2)快照日志获取容易,通过命令行、IDE 工具和 API 在开发态/运维态便捷获取日志;3)ArkUI 组件泄漏会导致绘制等相关的底层内存泄漏,这些底层内存泄漏分析相对比较复杂,容易造成干扰,影响其他 native 内存分析,可以通过修复组件泄漏减少干扰项,减少问题分析复杂度。
ArkUI Component 组件生命周期特点是在 aboutToDisappear 之后,即表示相关界面消失,组件已销毁,以 Component 为例,其完整的生命周期如下图:
当 Component 生命周期结束,进入到 DISAPPERED 状态后,后续无法被复用,不应该被继续持有,否则可能引发内存泄漏。
根据此特点,可以制定专门针对 ArkUI 组件的泄漏检测。其原理如下:
在应用侧,会对组件进行代码功能的调用,当生命周期结束后(如,页面退出),系统侧的 ArkUI 框架会自动给组件的对象打上标记,例如将 isDeleting_设置为 true。当 ArkTS OOM 发生时,ArkTS 运行时会先进行一次 GC,清理掉可回收的对象,然后再导出快照。如果在导出的快照中,发现 Component 的 isDeleting_为 true,则说明快照中 Component 对象没有按照预期释放,可能仍被其他对象持有,导致泄漏。
在多个应用线上 ArkTS OOM 泄漏问题分析中,通过此原理检测出的泄漏问题,准确率高达 90%+,对于内存定位非常有效。当前,在 HarmonyOS7.0 上,已经支持该检测的组件有:
1) BuilderNode、FrameNode、RenderNode、XComponent,这些组件生命周期结束后,ArkUI 框架会将对象上的_isDisposed 属性置为 true。
2) Ability,生命周期结束后,ArkUI 框架会将对象上的 isDestroyed 属性置为 true。
3) 自定义组件 Component,生命周期结束后,ArkUI 框架会将对象上的 isDeleting_属性置为 true。
备注:不同类型的组件生命周期结束的函数名称不一定相同,但检测原理是一致的。
在获取到 ArkTS 堆快照日志文件后,使用 mem-analyzer 即可高效完成 ArkUI 组件泄漏问题的分析与定位。mem-analyzer 是一个集开发态日志抓取和运维态日志分析的命令行工具。
https://gitcode.com/ancimoon/developtools_dfx_mem_analyzer/releases。
使用手册链接为:https://gitcode.com/ancimoon/developtools_dfx_mem_analyzer/blob/master/docs/usage.md。 ArkTS 堆快照日志获取方法可参考第 4 章节。
开发者可通过 mem-analyzer 命令行工具执行分析:mem-analyzer jsleak xx.rawheap -o
在处理过程中,分析工具会找到所有标记过的对象(属性 isDestroyed、_isDisposed、isDestroyed 为 true),并识别其是否为 ArkUI 组件(Component、BuilderNode、FrameNode、 RenderNode、Ability 等),然后按照对象类型进行聚类,计算大小、节点个数和引用链。之后,将信息输出为一份 html 文件,并自动打开该文件。文件示意如下:
在“ArkUI 组件/ArkTS 对象泄漏分析报告”这个 tab 中,详情列表中,按照 size,由大到小依次列出所有识别到的、可能发生泄漏的 ArkUI 组件。在表中,点击“详情”,可以查看此类型相关的所有引用链。引用链是一条从 Root 出发,经由中间对象,达到目标对象的链条,其中 A->B,说明 A 引用(持有)B。
mem-analyzer 对引用链里的特定类型进行了展开处理,提取出相关符号的业务特征信息,具体如下:
1)JSObject 对象,分析工具会读取此对象的属性总数、以及最多 10 个非系统的属性名字,以方便用户对应到真实的代码。系统公共属性,如 hclass、__proto__、ArkInternalHash、HashField 等会被直接过滤掉,不会显示。假设某个对象一共有 7 个属性,用户自定义的属性名为 id, alive, instance,则会显示为 JSObject(len=7, prop=id,alive,instance)。
2) string 对象,分析工具会截取真实值的前 20 个字符,并显示字符串长度的区间(长度<10KB, 长度>=10KB 并且<1MB,以及长度>=1MB)。例如,字符串“abc”会显示为 string(size:<10KB,content=abc)。在线上实际运行时,快照导出时会丢弃字符内容,因此会显示为 string(size:<10KB, content=)。
3) 装饰器对象,分析工具会找到此装饰器对象真实的 ArkTS 类名。例如,类名为 Foo 的对象被类装饰器(如 @Observed, @State 等)处理后,日志中引用链里名字会变成 declarative_frontend/stateMgmt.js#observedClass(line:xx),丢失业务特征,分析工具会将其还原,输出名字为 stateMgmt.js#observedClass(Foo)。
4) SourceTextModule 对象,是模块(Module)的运行时记录对象,其中包含了导出对象,堆快照中名字为 SourceTextModule,缺少业务信息。分析工具会找出此类对象的模块名,导出个数,导出名等信息,方便开发者直接找到相关的业务代码。输出结果示例为 SourceTextModule(nums=1, module=xx.ts),其中 nums 为导出条目的个数,module 为模块名。
在“引用链总览图中”这个 tab 中,对引用链进行聚合展示,可以看到关键的引用节点。由于原始的详情列表中的引用链可能会非常多,平铺展示,不利于交叉分析,因此按照应用链进行聚合。聚合后,可清楚看到不同泄漏点的相同中间节点,释放此中间节点可修复多个泄漏问题。
此分析还支持其他参数,以实现更多、更详细的分析:
ArkTS 对象无法释放的核心原因是至少有一条从 GCRoot 到该对象的引用链,常见的 GCRoot 包含 GlobalHandleRoot、LocalHandleRoot、VMRoot、FrameRoot,参考链接。在实际场景中,从 GCRoot 到对象可能有多条不同的引用链,如果需要释放该对象,那么需要断开从 GCRoot 到该对象的所有引用链。以如下的引用链为例:
此引用链中,相关的符号是已经编译过的 ts 文件。如果本地有编译过的.ts 文件,可以直接对应到代码。如果没有,则可以通过sourceMap.map进行反混淆。使用如下命令:
得到真实的源文件 LeakAbility.ets。在此引用链中可以看到:
1)泄漏对象为 LeakAbility,其持有者为 JSObject,此 JSObject 有名为 id, alive, instance 的属性。通过该属性定位到,其类型为 AbilityLeakEntry。
2) JSObject(len=7, prop=id,alive,instance)的持有者为 JSArray,也就是有个 AbilityLeakEntry 数组。
3)数组的持有者为 LexicalEnv,也就是一个词法环境。其上游为 LeakAbility.ets#LeakAbility(line:53)[entry]。也就是说词法环境属于 LeakAbility.ets 文件。在 LeakAbility.ets 中直接搜索 AbilityLeakEntry 数组,找到对象 leakedAbilityRegistry,其在 Ability 的 onCreate()函数中存储 LeakAbility 的 this 指针。
由此还原出完整的引用链。GlobalHandleRoot 持有 LeakAbility 对象,LeakAbility 对象持有 LeakAbility.ets 模块级词法环境 LexicalEnv,此环境中有个全局变量 leakedAbilityRegistry 数组,数组中的对象又引用了 LeakAbility 对象自身,构成循环引用:instance:this->LeakAbility->LexicalEnv->leakedAbilityRegistry->entry.instance->LeakAbility。
为了不影响代码的正常功能,选择在 LeakAbility 生命周期结束(onDestroy())时,取消引用。
根据之前的引用链总览图,可以看到 LeakAbility 只有这一条从 GCRoot 出发引用链。断开后,对象没有其他链引用,也就可以正常释放。
通过 DevEco AI + DFX Skills 可以自动化完成分析、定位与修复。步骤:
1. 下载 DFX Skills,下载链接:https://gitcode.com/openharmony-sig/developtools_dfx_skills
2. 通过 DevEco Studio 打开项目。
3. 在 DevEco Studio 右边栏找到并登录 CodeGenie。安装 arkts 代码修复 Skill,名字为 memoryleak-arkts-code-patch。
OpenHarmony API 中存在有很多的注册/注销监听的接口,如使用不当可能导致 ArkUI 组件泄漏。
访问原文 返回搜索 https://www.infoq.cn/article/4JX6Z7RZiN3mubeI2KyT
本页为82搜索生成的网页快照,内容来自站内索引;演示数据的原文链接仅作示例。