mcpField
I
ios-memgraph-analysis

ios-memgraph-analysis

github.com/dpearson2699开发1.2k1.3k 次安装13 周前更新
代码仓库

关于 ios-memgraph-analysis

面向 iOS 开发者的内存图分析技能,用于依据明确的对象生命周期区分不可达泄漏、可达状态增长、缓存和堆回归。强调保留原始证据,并在修复后复核相同类型及所有权路径。

主要功能

  1. 01定义对象释放边界并捕获内存图
  2. 02保留原始输出并生成有界摘要
  3. 03区分泄漏、可达增长与缓存
  4. 04追踪应用拥有的对象和引用路径
  5. 05按相同生命周期复测修复结果

适用场景

  1. 01调查页面关闭后仍存活的对象
  2. 02分析无泄漏报告但堆持续增长的情况
  3. 03验证缓存上限和驱逐行为
  4. 04对比修复前后的所有权路径

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dpearson2699/swift-ios-skills --skill ios-memgraph-analysis

安装后在 Claude Code 中输入/ios-memgraph-analysis确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
evals.json
references/
reachable-growth.md
scripts/
capture_sim_memgraph.py
summarize_memgraph.py

详细介绍

iOS Memgraph 分析

使用内存图证明内存为何会越过定义的生命周期边界继续存活。 区分不可达泄漏与可达增长,保留原始工具输出,并在修复后验证相同的应用自有类型和所有权路径。

目录

边界

本技能负责 .memgraph 捕获以及命令行所有权/增长分析。 如果交互式图表和分配时间线是主要任务,请使用 Memory Graph Debugger 或 Instruments。只有运行时证据识别出生命周期或路径后,才通过源代码审查调查疑似的闭包捕获。

证据模型

不要混淆以下情况:

  • 不可达泄漏: 已分配的内存不再有从存活根对象出发的路径。 孤立的强引用循环也可能不可达,但仍会占用内存。
  • 可达但已弃用的状态: 存活的根对象仍保留用户流程已不再需要的对象。leaks 正确报告为零也并不奇怪。
  • 预期中的缓存或对象池: 内存有意保持存活,应根据其上限、逐出行为和内存压力响应进行判断。
  • 堆回归或碎片化: 占用量增长是因为更多/更大的分配持续存在,或脏页利用率不佳,而非发生了泄漏。

Apple 的泄漏扫描器采用保守的指针发现机制,且类型元数据并不完整。计数可能波动,结果为零也不能证明不存在所有权问题。强有力的证据应能指出预期的生命周期、应用自有类型或分配,以及可信的路径或孤立复现。

工作流

1. 在捕获前定义生命周期

指出应当消失的对象,以及令其不再有用的事件。 例如:关闭编辑器并完成待处理的保存工作后,EditorViewModel 应当析构。

记录一套确定性的操作序列:

  1. 启动应用或恢复到已知状态;
  2. 可选:捕获基准内存图;
  3. 执行特性流程;
  4. 越过预期的释放边界;
  5. 等待合理的异步清理完成;
  6. 捕获流程结束后的内存图。

保持构建版本、模拟器/设备、数据、Malloc Stack Logging 设置和重复次数一致。Malloc Stack Logging 会提供有价值的分配回溯,但也会带来开销;只比较设置相同的运行结果。

2. 捕获内存图时不要猜测进程

Xcode 可以通过 Memory Graph Debugger 导出内存图。对于正在运行的 Simulator 应用,请使用本技能提供的辅助工具:

mkdir -p /tmp/myapp-memory
mkdir /tmp/myapp-memory/run-01
python3 scripts/capture_sim_memgraph.py \
  --bundle-id com.example.MyApp \
  --output-dir /tmp/myapp-memory/run-01 \
  --pretty > /tmp/myapp-memory/run-01/capture.json

每次运行时,mkdir 必须在捕获目录已存在时失败。请使用新的运行名称,而不是将旧证据与重试结果混在一起。

如果启动了多个 Simulator,请传入 --udid。辅助工具只接受一个完全匹配的 launchd 标签和 PID;匹配结果为零个或多个时都会报错。它会运行主机上的 leaks --outputGraph 命令,保留 stdout/stderr,并写入清单。不要将其替换为 pgrep | head -1 或子字符串匹配。

捕获操作会暂停进程。不要将捕获耗时用作性能数据。

3. 保留原始输出并生成有界摘要
MEMGRAPH=$(jq -er \
  'select(.status == "captured") | .memgraph | select(type == "string" and length > 0)' \
  /tmp/myapp-memory/run-01/capture.json)
test -s "$MEMGRAPH"
python3 scripts/summarize_memgraph.py \
  "$MEMGRAPH" \
  --artifact-dir /tmp/myapp-memory/run-01/analysis-raw \
  --app-image 'MyApp|MyFeatureKit' \
  --trace-limit 3 --group-by-type --pretty \
  > /tmp/myapp-memory/run-01/analysis.json

从保留的捕获报告中读取准确的图路径;不要猜测带时间戳的文件名。该辅助工具会创建专用的原始产物目录,拒绝复用该目录,运行 leaks --list,并且只解析其文本中的保守子集。--app-image 用于标记候选行;它不能证明归属关系。--trace-limit 会运行有界的 leaks --traceTree=<address> 查询。当聚合根路径比单个泄漏地址更有用时,添加 --reference-tree。使用 --group-by-type 时,该 reference-tree 查询会在同一次调用中按类型分组。 leaks 的退出状态 0 和 1 仍可分析;主查询状态高于 1 时,摘要生成失败;可选查询失败会被保留,并警告其不可用,但不会丢弃有效的主摘要。

Apple 未将这些文本格式发布为稳定的机器可读架构。应将解析警告视为检查原始产物的理由,而不是放宽解析器,直到它输出想要的答案。

4. 找到首个可采取行动的应用自有边

从应用自有的泄漏类型或分配栈入手。检查:

  • 泄漏对象图以及 Malloc Stack Logging 回溯(如果存在);
  • 有界的 --traceTree=<address>,用于查看引用一个地址的对象;
  • --groupByType,以压缩重复类型并揭示被保留的载荷;
  • 当责任地址不明确时,使用 --referenceTree 查看自顶向下的视图;
  • 源代码中由应用控制的首个强引用边。

不可达的自循环可能在 traceTree 中没有活动根。应使用分组后的泄漏图,并结合源代码验证,或将相关行为简化为独立复现。切勿编造图中不存在的根路径。

5. leaks 为空时调查增长

使用匹配的基线图和流程后图,定位增长区域,比较对象类型,然后追踪可疑地址,直到找到应用自有的边。证据应证明同一生命周期内存在持续的可达增长,而不是 RSS 值较低或单个较大的快照。仅在此无泄漏分支中加载 reachable-growth.md;其中包含按顺序执行的 vmmap、heap、leaks 和 malloc_history 查询,以及依赖日志记录情况的替代方案。

6. 修复并验证相同的生命周期

优先采用范围最小的所有权修正:断开非预期的强引用边,取消持有该对象的工作,移除观察者,限制缓存容量或淘汰缓存,或在大缓冲区最后一次使用后释放它。当引用可能合理地变为 nil 时使用 weak;只有在生命周期有明确保证时才使用 unowned。

重复执行完全相同的流程。如果同一应用自有类型或路径消失,或者在重复运行中归因于它的前后增长被消除,则可认为修复得到支持。仅 RSS 降低、图文件变小或聚合泄漏计数降低,都不足以作为证明。

所有权决策

证据后续操作
根循环中的应用类型检查两个强引用边和分配栈。
泄漏地址没有根检查分组后的循环证据,并隔离相关流程。
活动根保留了已关闭功能的状态沿路径找到首个应用自有的边。
没有泄漏,但 malloc 持续增长对比基线和流程后的堆对象。
框架对象占主导找出应用创建的所有者、输入或调用频率。
增长稳定在有记录的上限在修改前测试淘汰和内存压力行为。

常见错误

  • 因为 leaks 有一次返回了零,就断言应用没有泄漏。
  • 从含糊不清的列表中选择第一个 PID 或 Simulator。
  • 只在对比的一侧启用 Malloc Stack Logging。
  • 将解析器尽力而为生成的类型列当作 API 保证。
  • 未找到应用关联项,就把庞大的引用树粘贴到报告中。
  • 不考虑生命周期就用 [weak self] 修复每个闭包。
  • 未证明目标生命周期和所有权路径,就根据图的大小、RSS 或总数变化声称问题已解决。

审查清单

  • 已明确对象和预期释放边界。
  • 基线和流程后的图使用相同的构建版本、运行目标、数据状态、确定性流程、清理等待时间、重复次数和 Malloc Stack Logging 设置。
  • Simulator、bundle identifier、进程标签和 PID 均明确无歧义。
  • 已保留原始图和命令原始输出。
  • 当前已安装工具的帮助信息已确认随版本变化的命令格式。
  • 已区分泄漏、可达增长、预期缓存和内存碎片。
  • 调查结果指出了由应用拥有的类型或分配,以及可信的路径。
  • 已说明缺失的类型元数据或保守扫描器的限制。
  • 修复针对一个所有权或生命周期原因。
  • 验证重复了相同流程和证据查询。

参考资料

评论

支持 Markdown