ios-memgraph-analysis
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseiOS Memgraph Analysis
iOS Memgraph 分析
Use memory graphs to prove why memory survives a defined lifetime boundary.
Separate unreachable leaks from reachable growth, preserve raw tool output, and
verify the same app-owned type and ownership path after a fix.
使用内存图来证明内存为何能在定义的生命周期边界之外存续。区分不可达泄漏与可达增长,保留原始工具输出,并验证修复后相同的应用所属类型和所有权路径。
Contents
目录
Boundary
边界
This skill owns capture and command-line ownership/growth analysis.
Use the Memory Graph Debugger or Instruments when their interactive graph and
allocation timeline are the primary task. Use source review for a suspected
closure capture only after runtime evidence identifies the lifetime or path.
.memgraph本技能负责文件的捕获和命令行所有权/增长分析。当交互式图和分配时间线为主要任务时,请使用Memory Graph Debugger或Instruments。仅当运行时证据确定了生命周期或路径后,才对疑似闭包捕获进行源码审查。
.memgraphEvidence Model
证据模型
Do not collapse these conditions:
- Unreachable leak: allocated memory no longer has a path from a live root. An isolated strong cycle can be unreachable and still consume memory.
- Reachable but abandoned state: a live root still retains objects the user
flow no longer needs. may correctly report zero.
leaks - Expected cache or pool: memory survives intentionally and must be judged by its bound, eviction behavior, and pressure response.
- Heap regression or fragmentation: footprint grows because more/larger allocations persist or dirty pages are poorly utilized, without a leak.
Apple's leak scanner uses conservative pointer discovery and incomplete type
metadata. Counts can fluctuate, and a zero result does not prove the absence of
an ownership bug. Strong evidence identifies the expected lifetime, an
app-owned type or allocation, and a credible path or isolated reproduction.
请勿混淆以下情况:
- 不可达泄漏:已分配内存不再有从活动根节点出发的路径。孤立的强引用循环可能处于不可达状态但仍占用内存。
- 可达但已废弃状态:活动根节点仍保留用户流程不再需要的对象。工具可能会正确报告零泄漏。
leaks - 预期缓存或池:内存有意存续,需根据其边界、驱逐行为和压力响应进行判断。
- 堆回归或碎片化:内存占用增长是因为更多/更大的分配持续存在,或脏页利用率低下,但不存在泄漏。
Apple的泄漏扫描器使用保守的指针发现机制和不完整的类型元数据。计数可能会波动,零结果并不证明不存在所有权问题。有力的证据应明确预期生命周期、应用所属类型或分配,以及可信的路径或孤立复现场景。
Workflow
工作流程
1. Define the lifetime before capturing
1. 捕获前定义生命周期
Name the object that should disappear and the event that ends its useful life.
For example: should deinitialize after dismissing the editor
and completing pending save work.
EditorViewModelRecord one deterministic sequence:
- launch or restore a known state;
- take an optional baseline graph;
- perform the feature flow;
- cross the expected release boundary;
- wait for legitimate asynchronous cleanup;
- take the post-flow graph.
Keep build, simulator/device, data, Malloc Stack Logging setting, and repetition
count stable. Malloc Stack Logging adds valuable allocation backtraces but also
overhead; compare only runs with the same setting.
命名应消失的对象以及结束其有效生命周期的事件。例如:应在关闭编辑器并完成待处理保存工作后初始化。
EditorViewModel记录一个确定性流程:
- 启动或恢复已知状态;
- 可选:获取基准内存图;
- 执行功能流程;
- 跨越预期的释放边界;
- 等待合法的异步清理完成;
- 获取流程后的内存图。
保持构建版本、模拟器/设备、数据、Malloc Stack Logging设置和重复次数稳定。Malloc Stack Logging会添加有价值的分配回溯信息,但也会带来额外开销;仅对比设置相同的运行实例。
2. Capture a graph without guessing the process
2. 无需猜测进程即可捕获内存图
Xcode can export a graph from the Memory Graph Debugger. For a running Simulator
app, use the helper from this skill:
bash
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.jsonThe per-run must fail if the capture directory already exists. Use a
new run name rather than mixing stale evidence with a retry.
mkdirPass when more than one Simulator is booted. The helper accepts only
one exact launchd label and PID; zero or multiple matches are errors. It runs the
host command, retains stdout/stderr, and writes a manifest.
Do not replace this with or a substring match.
--udidleaks --outputGraphpgrep | head -1Capturing suspends the process. Do not use capture latency as performance data.
Xcode可以从Memory Graph Debugger导出内存图。对于运行中的模拟器应用,使用本技能提供的辅助脚本:
bash
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当多个模拟器启动时,传入参数。辅助脚本仅接受一个精确的launchd标签和PID;零匹配或多匹配均视为错误。它会运行主机的命令,保留标准输出/错误输出,并写入清单文件。请勿用或子字符串匹配替代此脚本。
--udidleaks --outputGraphpgrep | head -1捕获操作会暂停进程。请勿将捕获延迟作为性能数据。
3. Preserve raw output and build a bounded summary
3. 保留原始输出并生成有限范围的摘要
bash
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.jsonRead the exact graph path from the preserved capture report; do not guess a
timestamped filename. The helper creates a dedicated raw-artifact directory,
refuses to reuse it, runs , and parses only a conservative subset
of its text. marks candidate rows; it does not prove ownership.
runs bounded
queries. Add when aggregate
root paths are more useful than individual leaked addresses. With
, that reference-tree query is grouped in the same invocation.
Exit statuses 0 and 1 from remain analyzable; a primary status above 1
fails the summary, while optional-query failures are preserved and warned as
unusable without discarding a valid primary summary.
leaks --list--app-image--trace-limitleaks --traceTree=<address>--reference-tree--group-by-typeleaksApple does not publish these text formats as stable machine schemas. Treat
parse warnings as a reason to inspect the raw artifacts, not to loosen the
parser until it emits a desired answer.
bash
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从保存的捕获报告中读取精确的内存图路径;请勿猜测带时间戳的文件名。辅助脚本会创建专用的原始工件目录,拒绝重复使用,运行命令,并仅解析其文本内容的保守子集。标记候选行;但不证明所有权。会运行有限范围的查询。当聚合根路径比单个泄漏地址更有用时,添加参数。结合参数,该引用树查询会在同一调用中进行分组。工具的退出状态0和1仍可分析;主状态大于1时会导致摘要失败,而可选查询失败会被保留并警告为不可用,但不会丢弃有效的主摘要。
leaks --list--app-image--trace-limitleaks --traceTree=<address>--reference-tree--group-by-typeleaksApple未将这些文本格式发布为稳定的机器模式。将解析警告视为检查原始工件的理由,而非放宽解析器直到输出预期结果。
4. Find the first actionable app-owned edge
4. 找到第一个可操作的应用所属边
Start with an app-owned leaked type or allocation stack. Inspect:
- the leak's object graph and Malloc Stack Logging backtrace, when present;
- a bounded for objects that reference one address;
--traceTree=<address> - to compress repeated types and reveal a retained payload;
--groupByType - for a top-down view when the responsible address is unclear;
--referenceTree - source code for the first strong edge controlled by the app.
An unreachable self-cycle may have no live root in . Use the grouped
leak graph plus source verification or reduce the behavior to an isolated
reproduction. Never invent a root path that the graph does not contain.
traceTree从应用所属的泄漏类型或分配栈开始。检查:
- 泄漏的对象图和Malloc Stack Logging回溯(如果存在);
- 针对引用某一地址的对象的有限范围查询;
--traceTree=<address> - 参数,用于压缩重复类型并显示保留的负载;
--groupByType - 当责任地址不明确时,使用获取自上而下的视图;
--referenceTree - 应用控制的第一个强引用边的源代码。
不可达的自循环在中可能没有活动根节点。使用分组泄漏图加源码验证,或将行为简化为孤立复现场景。切勿编造内存图中不存在的根路径。
traceTree5. Investigate growth when leaks
is empty
leaks5. 当leaks
结果为空时调查增长情况
leaksUse matching baseline and post-flow graphs, locate the growing region, compare
object types, then trace a suspicious address back to an app-owned edge. The
evidence goal is persistent reachable growth across the same lifetime—not a
lower RSS value or a single large snapshot. Load
reachable-growth.md only for this empty-leak
branch; it contains the ordered , , , and
queries and their logging-dependent alternatives.
vmmapheapleaksmalloc_history使用匹配的基准和流程后内存图,定位增长区域,对比对象类型,然后将可疑地址追溯到应用所属边。证据目标是同一生命周期内的持续性可达增长——而非更低的RSS值或单个大快照。仅在这种空泄漏分支下加载reachable-growth.md;它包含有序的、、和查询及其依赖日志的替代方案。
vmmapheapleaksmalloc_history6. Fix and verify the same lifetime
6. 修复并验证相同生命周期
Prefer the narrowest ownership correction: break the unintended strong edge,
cancel work that owns the object, remove an observer, bound/evict a cache, or
release a large buffer after its last use. Use when the reference may
legitimately become ; use only with a proven lifetime guarantee.
weaknilunownedRepeat the identical flow. A fix is supported when the same app-owned type/path
disappears or the pre/post growth attributable to it is removed across repeated
runs. Lower RSS, a smaller graph file, or a lower aggregate leak count alone is
not proof.
优先选择最窄范围的所有权修正:打破意外的强引用边,取消拥有该对象的任务,移除观察者,限制/驱逐缓存,或在大型缓冲区最后一次使用后释放它。当引用可能合法变为时使用;仅在有明确生命周期保证时使用。
nilweakunowned重复完全相同的流程。当相同的应用所属类型/路径消失,或其导致的修复前后增长在多次运行中被消除时,修复才被确认有效。仅更低的RSS、更小的内存图文件或更低的总泄漏计数不足以作为证据。
Ownership Decisions
所有权决策
| Evidence | Next action |
|---|---|
| App type in a root cycle | Inspect both strong edges and allocation stack. |
| No root for a leaked address | Inspect grouped cycle evidence and isolate the flow. |
| Live root retains dismissed feature state | Follow the path to the first app-owned edge. |
| Zero leaks but repeated malloc growth | Diff baseline/post heap objects. |
| Framework object dominates | Find the app-created owner, input, or call frequency. |
| Growth stabilizes at a documented bound | Test eviction/pressure behavior before changing it. |
| 证据 | 下一步操作 |
|---|---|
| 根循环中的应用类型 | 检查强引用边和分配栈。 |
| 泄漏地址无根节点 | 检查分组循环证据并隔离流程。 |
| 活动根节点保留已关闭的功能状态 | 追溯路径至第一个应用所属边。 |
| 零泄漏但重复malloc增长 | 对比基准/流程后的堆对象。 |
| 框架对象占主导 | 找到应用创建的所有者、输入或调用频率。 |
| 增长稳定在文档化的边界 | 在修改前测试驱逐/压力行为。 |
Common Mistakes
常见错误
- Declaring the app leak-free because returned zero once.
leaks - Selecting the first PID or Simulator from an ambiguous list.
- Enabling Malloc Stack Logging in only one side of a comparison.
- Treating a parser's best-effort type column as an API guarantee.
- Pasting enormous reference trees into a report without finding an app edge.
- Fixing every closure with without reasoning about lifetime.
[weak self] - Claiming success from graph size, RSS, or total-count changes without proving the target lifetime and ownership path.
- 因一次返回零就宣称应用无泄漏。
leaks - 从不明确的列表中选择第一个PID或模拟器。
- 仅在对比的一侧启用Malloc Stack Logging。
- 将解析器的最佳类型列视为API保证。
- 在未找到应用边的情况下,将巨大的引用树粘贴到报告中。
- 不考虑生命周期就用修复所有闭包。
[weak self] - 在未证明目标生命周期和所有权路径的情况下,仅凭内存图大小、RSS或总数变化宣称成功。
Review Checklist
审核清单
- The object and expected release boundary are explicit.
- Baseline and post-flow graphs use the same build, runtime target, data state, deterministic flow, cleanup wait, repetitions, and Malloc Stack Logging setting.
- Simulator, bundle identifier, process label, and PID are unambiguous.
- Original graph and raw command outputs are preserved.
- Current installed-tool help confirms version-sensitive command shapes.
- Leak, reachable growth, expected cache, and fragmentation are separated.
- The finding names an app-owned type/allocation and credible path.
- Missing type metadata or conservative-scanner limits are disclosed.
- The fix changes one ownership/lifetime cause.
- Verification repeats the same flow and evidence query.
- 对象和预期释放边界明确。
- 基准和流程后内存图使用相同的构建版本、运行时目标、数据状态、确定性流程、清理等待时间、重复次数和Malloc Stack Logging设置。
- 模拟器、Bundle ID、进程标签和PID明确无误。
- 原始内存图和原始命令输出已保留。
- 当前安装的工具帮助文档确认了版本敏感的命令格式。
- 泄漏、可达增长、预期缓存和碎片化已区分。
- 发现结果命名了应用所属类型/分配和可信路径。
- 已披露缺失的类型元数据或保守扫描器限制。
- 修复仅更改了一个所有权/生命周期原因。
- 验证重复了相同的流程和证据查询。
References
参考资料
- Reachable growth when is empty — matched-graph comparison and address-to-owner workflow
leaks - Gathering information about memory use
- Detect and diagnose memory issues — WWDC21
- Analyze heap memory — WWDC24
- 当为空时的可达增长 — 匹配内存图对比和地址到所有者的工作流程
leaks - 收集内存使用信息
- 检测和诊断内存问题 — WWDC21
- 分析堆内存 — WWDC24