关于 ios-memgraph-analysis
面向 iOS 开发者的内存图分析技能,用于依据明确的对象生命周期区分不可达泄漏、可达状态增长、缓存和堆回归。强调保留原始证据,并在修复后复核相同类型及所有权路径。
主要功能
- 01定义对象释放边界并捕获内存图
- 02保留原始输出并生成有界摘要
- 03区分泄漏、可达增长与缓存
- 04追踪应用拥有的对象和引用路径
- 05按相同生命周期复测修复结果
适用场景
- 01调查页面关闭后仍存活的对象
- 02分析无泄漏报告但堆持续增长的情况
- 03验证缓存上限和驱逐行为
- 04对比修复前后的所有权路径
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill ios-memgraph-analysis安装后在 Claude Code 中输入/ios-memgraph-analysis确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/reachable-growth.mdscripts/capture_sim_memgraph.pysummarize_memgraph.py详细介绍
iOS Memgraph 分析
使用内存图证明内存为何会越过定义的生命周期边界继续存活。 区分不可达泄漏与可达增长,保留原始工具输出,并在修复后验证相同的应用自有类型和所有权路径。
目录
边界
本技能负责 .memgraph 捕获以及命令行所有权/增长分析。
如果交互式图表和分配时间线是主要任务,请使用 Memory Graph Debugger 或 Instruments。只有运行时证据识别出生命周期或路径后,才通过源代码审查调查疑似的闭包捕获。
证据模型
不要混淆以下情况:
- 不可达泄漏: 已分配的内存不再有从存活根对象出发的路径。 孤立的强引用循环也可能不可达,但仍会占用内存。
- 可达但已弃用的状态: 存活的根对象仍保留用户流程已不再需要的对象。
leaks正确报告为零也并不奇怪。 - 预期中的缓存或对象池: 内存有意保持存活,应根据其上限、逐出行为和内存压力响应进行判断。
- 堆回归或碎片化: 占用量增长是因为更多/更大的分配持续存在,或脏页利用率不佳,而非发生了泄漏。
Apple 的泄漏扫描器采用保守的指针发现机制,且类型元数据并不完整。计数可能波动,结果为零也不能证明不存在所有权问题。强有力的证据应能指出预期的生命周期、应用自有类型或分配,以及可信的路径或孤立复现。
工作流
1. 在捕获前定义生命周期
指出应当消失的对象,以及令其不再有用的事件。
例如:关闭编辑器并完成待处理的保存工作后,EditorViewModel 应当析构。
记录一套确定性的操作序列:
- 启动应用或恢复到已知状态;
- 可选:捕获基准内存图;
- 执行特性流程;
- 越过预期的释放边界;
- 等待合理的异步清理完成;
- 捕获流程结束后的内存图。
保持构建版本、模拟器/设备、数据、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 均明确无歧义。
- 已保留原始图和命令原始输出。
- 当前已安装工具的帮助信息已确认随版本变化的命令格式。
- 已区分泄漏、可达增长、预期缓存和内存碎片。
- 调查结果指出了由应用拥有的类型或分配,以及可信的路径。
- 已说明缺失的类型元数据或保守扫描器的限制。
- 修复针对一个所有权或生命周期原因。
- 验证重复了相同流程和证据查询。
参考资料
leaks结果为空时的可达增长 — 匹配图对比和地址到所有者的工作流- 收集有关内存使用情况的信息
- 检测和诊断内存问题 — WWDC21
- 分析堆内存 — WWDC24