一、背景

分时页优化前的线上版本存在两个典型内存问题:

  • 单次打开分时页时,进程内存申请峰值约为 220 MB;离开页面后,仍有约 110 MB 留存。
  • 线上 OOM 率处于较高水平。

优化收益方面,在同一线上监控口径下,优化前稳定版本 H037.09.024 与优化后稳定版本 H037.09.027 对比,万人 OOM 率从 41.6 降低到 6.09。

需要说明的是,线上指标通常会受到版本内容、用户分布、采样窗口和设备环境等因素影响。本文把 OOM 率变化作为问题背景和治理收益参考,不把它单独作为某个分析脚本或单个修复点的因果证明。

本文重点讨论的是:如何借助 Claude Code 把一次内存分析过程沉淀为后续可复用的工程资产。

把一次分析变成可复用资产

很多 AI 开发工作流停留在“当前会话给出一个结论”,围绕结论不断迭代提问。

这种方式在复杂工程问题里容易出现三个缺点:

  • 会话结束后,资料、判断过程和脚本散落在上下文里,下次需要重新解释背景。
  • 输出缺少稳定入口,换一个样本或换一个开发者后,流程难以复现。
  • 结论依赖单次模型推理,缺少脚本化证据和人工复核边界。

本次实践中,将每轮分析产出有意识地落到文件系统,并按用途拆成五类“可持久化内容”:

类别

载体

示例

领域知识

Markdown / Skill

HarmonyOS Snapshot 分析笔记、ArkTS GC 资料、v8-heapsnapshot-format-spec.md 参考材料

分析方法论

Skill

harmonyos-memory-leak-analysis-skill.md

分析工具

Python 脚本

analyze_insight.py 与 lib/analysis/tools 分层脚本

输出规范

报告 Skill

harmonyos-leak-report-writing.md

执行入口

Prompt 模板

采集、分析、复核三类 Prompt

这些内容的目标不是替代开发者判断,而是把重复性工作固化下来:资料可以被再次加载,脚本可以被再次执行,报告结构可以被再次套用,模型也可以在新会话里更快进入同一套问题空间。

二、数据对象与分析边界

本文先约定几个术语边界。

Snapshot:本文中的 snapshot 主要指 DevEco Profiler 导出的 .insight 样本中包含的 ArkTS 堆快照文件,常见后缀为 .heapsnapshot。它不是泛指所有 HarmonyOS 内存数据,也不等同于 Native Heap 或内核态内存数据。

.insight 文件:在本次样本中,.insight 文件可以按 ZIP 包解压,解压后包含一个或多个 .heapsnapshot、PSS 相关数据库、native hook 数据库和会话描述文件。不同 DevEco Studio 版本或不同采集模板的文件组织可能存在差异,因此投稿时更稳妥的表述是“在本次样本中观察到的结构”,而不是把它写成所有 .insight 文件的固定格式。

.heapsnapshot 格式:本次样本中的 .heapsnapshot 使用 JSON 表达对象图,核心字段包含 snapshot.metanodesedgesstrings 等。从字段组织和图分析方式看,它与 Chrome/V8 heap snapshot 有可类比之处,因此 V8/Chrome DevTools 的公开资料可以用于理解通用术语和分析方法。但实际分析对象仍然是 HarmonyOS/ArkTS 堆快照,节点语义、对象命名和 GC 行为必须以本次样本、ArkTS/OpenHarmony 源码和公开文档为准,不能把 V8 结论直接套用到 ArkTS 上。

循环引用与泄漏:真正需要关注的是:该循环结构是否仍可从 GC Root、模块作用域、全局注册表、事件总线或其他长生命周期对象到达。只有仍然可达,环及其下游对象才会持续保活。

Skill:本文所称 Skill 主要指在本项目中通过 ECC 插件沉淀出的可复用知识文件和流程文件。Claude Code 官方 Skills 通常以 SKILL.md 作为入口文件,具备目录结构、元信息和自动加载机制。本文中的 ECC learn 产物与官方 Skills 机制目标相似,都是为了跨会话复用知识,但具体文件组织和触发方式需要按实际工具链区分。

输入数据:一个 .insight 样本

分析开始时,可用输入非常有限:一个由 DevEco Profiler 导出的 .insight 文件。

在本次样本中,解压后的目录结构近似如下:

my_session/
├── HEAP-1770961547611-14617.heapsnapshot   # ArkTS 堆快照,JSON 对象图结构
├── HEAP-1770961558481-14617.heapsnapshot   # 多个采样时刻的堆快照
├── xxx_memory.db                           # PSS 等内存分区数据
├── xxx_native_hook.db                      # native 分配和调用栈相关数据
└── manifest.json                           # 会话信息

对 Agent 来说,这类数据并不是默认就能准确理解的。它需要知道 .heapsnapshot 的数组压缩结构、node_fields 与 edge_fields 的含义、ArkTS GC 相关背景,以及如何从 GC Root、引用链、依赖图和环结构推导可疑保活路径。由于公开资料中对 HarmonyOS Snapshot 内部结构、内存分析实践和真实案例的系统说明相对有限,前期工作的重点是先围绕鸿蒙源码和实际样本建立知识基础,再把可复用部分固化下来。

三、知识沉淀与工具链建设

先围绕鸿蒙源码和样本补齐知识

本次分析的对象是 HarmonyOS/ArkTS 产生的 heap snapshot,结论必须来自实际 .insight 样本、ArkTS/OpenHarmony 源码以及通过 Context7、Playwright 搜集到的公开文档信息。只是 HarmonyOS 侧公开的内存分析实践文档和案例相对有限,因此需要借鉴 V8/Chrome DevTools heap snapshot 中已经成熟的图结构分析方法,例如对象图、retainer path、双快照对比和可达性判断。这里的借鉴只用于理解通用分析思路和工具设计,不能替代对鸿蒙源码与样本数据的验证。

资料准备方式

本次使用的资料分为四类:

资料类型

用途

ArkTS 引擎源码

作为主要依据,理解 ArkTS 运行时、GC 和 heap snapshot 相关实现

OpenHarmony 源码

作为主要依据,理解系统侧对象、组件生命周期和框架行为

Context7 / Playwright / Web Search 搜集的公开文档

补充 HarmonyOS、DevEco Profiler、ArkTS、GC 和内存分析相关资料

V8 / Chrome DevTools 资料

作为参考材料,借鉴对象图、retainer path、双快照对比和可达性判断等通用分析方法

在实践中,可以通过人工提供、本地源码检索、Web Search、Context7 和 Playwright 等方式获取资料。重点不是固定使用某个检索插件,而是让外部资料能够落盘,并在后续会话中被重新加载。

本次给 Claude 提供或引导其检索的资料包括:

  • ArkTS 引擎源码:~/Desktop/project/arkcompiler_ets_runtime
  • OpenHarmony 源码:~/Desktop/project/OpenHarmony/code
  • V8 源码:https://github.com/v8/v8
  • 通过 Context7、Playwright 搜集到的 HarmonyOS / DevEco Profiler / ArkTS 公开文档信息

V8 源码和 Chrome DevTools heap snapshot 资料只作为外部参考,用于借鉴对象图、引用路径、双快照对比等通用分析方法,不作为 HarmonyOS 内存结论的直接依据。

本次使用过的三类 Prompt 如下。

Prompt 1:采集 HarmonyOS 内存分析相关资料

需要对'左右滑动+分时日线周线五日线.insight'进行内存泄漏分析,
这是一个 HarmonyOS .insight 样本,包含多次 ArkTS heap snapshot。
请通过 Context7 和 Playwright 探索 HarmonyOS / DevEco Profiler / ArkTS 内存分析相关资料,
将内存泄漏、快照分析、GC、组件生命周期相关资料整理为 Markdown,
保存到当前目录的 docs/office 目录下。

Prompt 2:补齐 GC 与 heap snapshot 背景

需要对'左右滑动+分时日线周线五日线.insight'进行内存泄漏分析,
这是一个 HarmonyOS .insight 样本,分析结论必须以 ArkTS/OpenHarmony 源码、公开文档和样本数据为准。
请阅读本地 ArkTS 引擎源码、OpenHarmony 源码,
通过 Context7、Playwright 搜集 HarmonyOS / DevEco Profiler / ArkTS 公开文档,
并参考 V8 / Chrome DevTools heap snapshot 资料中的通用对象图分析方法,
整理一份专业、客观、详细的 GC 与 heap snapshot 分析背景报告。
报告需要说明:哪些结论来自鸿蒙源码和样本,哪些内容只是外部资料的借鉴。

Prompt 3:沉淀 snapshot 分析指导

请基于已整理的 ArkTS/OpenHarmony 源码、公开文档、本次样本结构和外部参考资料,总结 HarmonyOS heap snapshot 的数据结构、解析方法和内存泄漏定位流程。
重点覆盖 nodes / edges / strings / meta、retainer path、依赖图、双快照对比和循环引用识别。
请将结果保存为可复用的 md 指导文档。

这三类 Prompt 的共同点是:明确任务对象,明确资料来源,明确落盘目录,明确后续要复用。

知识准备后的效果

完成资料准备后,Claude 已能辅助完成以下任务:

  • 结合样本中的 meta 信息,解释 .heapsnapshot 中 node_fieldsedge_fieldsnode_typesedge_types 等结构。
  • 理解 GC Root、引用链、对象保活和泄漏判断之间的关系。
  • 初步生成解析脚本,对多个 snapshot 做趋势、对比和可疑对象提取。

到这一步,知识仍然分散在会话上下文和 Markdown 文档里。下一步要解决的是:如何让这些知识在新会话里被稳定复用。

用 Skill 固化知识

把资料保存为 Markdown,开新对话时就需要重新读取,同时需要占用大量的上下文,需要将他们进行总结归纳,并将长篇内容做成外部引用。

对于跨会话复用而言,每次只喂关键信息:什么场景下应该加载哪些知识,按什么步骤执行,输出应该符合什么结构。

本项目使用 ECC 插件的 learn 能力,把当前会话中已经验证过的知识和流程整理为可复用 Skill。

本项目中沉淀的 Skill 大致分为三类:

Skill 类型

解决的问题

示例

领域知识 Skill

让模型理解 ArkTS/GC 语义,并借鉴 heap snapshot 通用图分析方法

HarmonyOS Snapshot 分析笔记、v8-heapsnapshot-format-spec.md

流程 Skill

让模型按固定步骤分析 .insight 样本

harmonyos-memory-leak-analysis-skill.md

输出 Skill

让模型按统一结构生成报告和复核清单

harmonyos-leak-report-writing.md

这种组织方式的好处是:知识、流程和输出规范被拆开维护。后续换一个模块或换一个 snapshot 样本时,通常只需要调整业务配置和 Prompt,而不需要重新解释 HarmonyOS snapshot 的样本结构、ArkTS 运行时背景和通用对象图分析方法。

构建脚本化工具链

有了前置知识后,Claude 不再只是解释单个快照,而是可以参与工具链设计。实践中先让 Claude 基于已有 Skill 和项目背景产出分析计划,再把计划拆成可执行脚本。

示例规划 Prompt:

/ecc:plan 设计一个详细的内存泄漏分析计划。
目标是对“左右滑动+分时日线周线五日线.insight”进行并行内存泄漏分析,
并生成专业、可复核的分析报告。
请将计划保存到项目的 plan 目录下。

后续迭代围绕几个核心问题展开:

  • 如何基于样本 meta 解析 .heapsnapshot 中的压缩数组结构。
  • 如何构建节点和边组成的对象图。
  • 如何寻找从 GC Root 到目标对象的 retainer path。
  • 如何做双快照对比,找出增长对象类型。
  • 如何识别循环引用,并把关键回边可视化。

经过多轮迭代,脚本从零散文件演进为分层工具集。顶层是一键入口 analyze_insight.py,底下按“基础层 / 单快照分析 / 辅助工具”组织,并通过可选的 leak_config.json 支持跨项目复用。

analyze_insight.py               # 一键入口,自动编排以下脚本
│
├── lib/                         # 共享基础层,被 import,不直接执行
│   ├── heap_core.py             # 数据加载与图构建
│   └── leak_config.py           # 业务配置加载
│
├── analysis/                    # 单快照与双快照分析
│   ├── retainer_path.py         # 引用链追溯
│   ├── cycles.py                # 环检测
│   ├── compare.py               # 双快照对比
│   ├── dominator_tree.py        # 可选:支配树与保留量辅助分析
│   ├── visualize.py             # 交互式 HTML 可视化
│   └── pss.py                   # PSS 分区趋势
│
└── tools/                       # 辅助工具
    ├── snapshot_timeline.py     # 多快照时间线对比
    ├── inspect_node.py          # 单节点深度探查
    ├── view_tracker.py          # ViewPU 组件追踪
    └── clean_app_cache.sh       # 设备缓存清理

一条命令可以跑完整条分析流水线:

python3 script/analyze_insight.py path/to/xxx.insight

产物按固定目录组织:

script/out/{insight_name}/
├── overview.txt                 # 多快照趋势
├── pss.txt                      # PSS 分区趋势
├── S1/                          # 单快照分析
├── S2/
├── S3/
└── S1_vs_S3/                    # 双快照对比

脚本执行顺序也被 harmonyos-memory-leak-analysis-skill.md 固化为标准分析流程:

  1. 解压 .insight,识别快照、PSS 数据和 native 数据。
  2. 运行 snapshot_timeline,确认整体增长阶段。
  3. 运行 pss,判断增长主要来自 ArkTS heap、native heap 还是其他分区。
  4. 运行 compare,找出基线快照与峰值快照之间增长最多的对象类型。
  5. 运行 retainer_path,从 GC Root 追踪可疑对象的保活路径。
  6. 运行 cycles,识别闭包自引用、组件互引用等环结构。
  7. 运行 dominator_tree,辅助查看对象保留量和保留范围。
  8. 运行 visualize,生成可交互依赖图,用于人工核对关键引用链和回边。

上图展示的是完整工具链能力,这一阶段的关键变化是:知识不再停留在说明文档中,而是被固化为脚本、配置和执行顺序。

模型的作用也从“自由发挥分析”变成“按既定方法编排工具、解释结果、补充报告”。

执行分析并生成报告

实际使用时,并不一定由开发者手动执行每个脚本,而是把任务交给 Claude,由它根据 Skill 匹配流程并调用一键入口。

Prompt:

path/to/左右滑动+分时日线周线五日线.insight 是一个鸿蒙snapshot数据。
快照时序为:S1 打开页面前,S2 首次进入分时页,S3 多次切股后,S4 离开页面后,S5 等待一段时间后。
请按 harmonyos-memory-leak-analysis-skill 的流程分析内存泄漏,
运行已有脚本,并将分析报告生成到指定目录。

Agent 收到任务后,会读取相关 Skill,调用 analyze_insight.py 跑完整条流水线,并根据 overview.txtpss.txtretainer.jsoncompare.txt、可选的 dominator.json 和可视化结果生成报告。脚本执行阶段可以高度自动化,但关键结论仍需人工抽样复核,尤其是以下几类判断:

  • 采集到的对象是否与实际业务逻辑对应。
  • 修复方案是否正确有效,代码修改是否会影响到其他模块,修复建议是否会破坏页面生命周期或事件订阅语义。
  • 分析报告中的增长对象是否排除了正常缓存、懒加载等情况。

四、案例:DrawLineStorageOwner 覆盖未释放

业务场景

分时页支持用户左右滑动切换股票。按预期,切股后旧股票相关数据和不再使用的组件引用应被释放;但在本次样本中,旧组件树没有释放干净,并在多次切股后持续累积。

本案例主要由 Claude/Opus 调用前述脚本生成初稿结论,再经过人工复核和整理。

引用路径与环结构给出关键线索

retainer.txt 文本版记录了目标对象到 GC Root 的最短线性路径;retainer.json 中额外包含脚本识别到的 cycles 字段。其中 4 个环与 DrawLineStorageOwner 相关,对应多次切股留下的多份独立副本。

DrawLineStorageOwner 把注册出去的闭包又存回自身 pushEvent 字段,形成 owner 与 closure 之间的闭合环。但需要强调的是,真正导致持续保活的不是“有环”本身,而是这个环仍然存在外部锚点:一条 -subroot- 边将 closure 挂到 GC Root 下,另一个 LexicalEnv 从模块作用域指向 owner。只要任一外部锚点存在,该环及其下游组件树就仍可能无法回收。

依赖图可视化辅助人工核对

visualize.py 生成的 S3/vis.html 将关键依赖图和环结构画出。回边以红色虚线标记,Cycles 面板按环编号列出多份副本,方便人工核对每一份残留对象是否对应一次切股。

五、复盘与可复用经验

这套流程的自动化程度并不均匀。适合脚本化的部分应尽量固定下来,需要业务判断的部分则必须保留人工复核。

环节

自动化程度

人工介入点

.insight 解压与快照规整

脚本自动化

通常不需要

timeline / PSS 初筛

脚本自动化

确认采样时序是否正确

compare / retainer / cycles 定位

脚本自动化

抽样核对关键引用链和环结构

dominator_tree 辅助分析

脚本自动化

在需要保留量视角时使用

根因归纳与代码映射

半自动

阅读源码并确认生命周期语义

修复实现

人工为主,AI 辅助

Code Review、回归验证

Codex 交叉复核

半自动

分歧点人工裁决

最终报告生成

模板辅助

结论复核、表达润色

本项目还使用 Codex 做交叉验证。做法是将相同背景资料、脚本和 snapshot 提供给 Codex,让它独立跑一遍分析流程,再将 Claude 与 Codex 的报告互相复核。两边一致的结论可信度更高;两边分歧的地方则优先交给人工复核。

这种“脚本证据 + 模型解释 + 人工决策”的组合,比单次模型回答更适合复杂内存问题。模型擅长整合资料、生成脚本和解释现象;脚本负责可重复计算;开发者负责判断业务生命周期、确认源码语义和决定修复策略。

可复用经验

本次实践中,最有复用价值的不是某一个具体 Prompt,而是下面的规范:

第一,先落盘,再迭代。 搜索到的资料、调通的脚本、确认过的结论都应保存到项目目录或 Skill 目录。只存在对话窗口里的知识,很难在下一次问题中稳定复用。

第二,区分样本事实、模型推断和人工结论。 timeline、compare、retainer path、cycles 输出属于样本事实;“疑似事件总线注册”属于基于路径的推断;最终根因和修复建议必须经过人工结合源码确认。

第三,脚本优先处理可规则化工作。 解压、解析、排序、对比、路径搜索、可视化都适合脚本化。模型应更多承担编排、解释和生成报告的工作,而不是每次重新“凭感觉”判断。

第四,报告模板也需要版本化。 内存分析报告不是简单列 Top N 对象,而应包括采样时序、增长阶段、PSS 分区、保活路径、可疑根因、排除项、修复建议和验证方式。

结语

这次实践表明,Claude Code 在复杂内存问题中的价值,不只在于“回答一次问题”,更在于帮助开发者把知识、脚本和判断流程沉淀成可复用资产。

这份文档实践于 26 年 2 月,具有一定时效性。截至 2026 年 4 月,相关工具仍在快速变化,新出的开源项目和插件可能会覆盖本文中的部分步骤。

相对稳定的是从学习到输出的迭代过程:学习 → 输出 → 校验 → 再学习。本文只是把这套方法迁移到 AI 辅助开发和内存分析场景中。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐