# 数字和关系的准确含义

## 输入与增量读取

V8 快照包含 metadata、扁平 nodes/edges 数组和 strings 表。Decoder 逐块接收文本，将主要数值数组直接读为 UInt64 记录，不构造全文件 JSON 语法树。字符串逐条解码；跨块转义、整数溢出和尾部完整性都会检查。源码位置、分配函数和分配树使用独立、受限的元数据解析。

解析器以 node_fields/edge_fields 定位列；边目标是原始节点数组偏移，必须对齐。对象 ID 最大支持 18446744073709551615，始终保留精确十进制字符串；字节数合计限制为 9007199254740991。

拒绝重复 ID/JSON 键/字段名、越界字符串、无效目标、不一致数量、缺失字段、未知边类型、负数和非整数记录。允许额外数值列，以及 V8 在字段类型描述末尾多给出的描述；记录宽度仍由字段数决定。默认根是第一个节点，必须是 synthetic。输入不支持采样 .heapprofile、JVM HPROF 或原生 MoonBit 运行时堆。

## snapshot-strong-v1

对 source → target：

1. weak 边不参与保留关系。
2. self 边不参与保留遍历；循环检测另行报告强自引用。
3. shortcut 只在 source 是根时参与。
4. property/context/internal/element/hidden 的其余边参与。

原始边均可查询，retaining 标明是否参与保留算法。

V8 的 WeakMap 条件关系会用带有 part of key 与 WeakMap 的 internal 边描述。MoonHeap 统计这些边，但仍按普通保留边计算。真实 GC 的 ephemeron 条件不能简化为一般强边，因此其保留体积可能与实际回收行为不同。

Chrome DevTools 还有页面归属、调试器和 WeakMap 专用规则。MoonHeap 不实现所有启发式，也不把不可达节点重新挂到根上；不承诺每一项 retained size 与 Chrome 相同。本项目没有复制 Chrome 的实现代码。

## 可达性、支配与引用路径

根可达性及最短路径采用 BFS。若从根到 v 的所有路径都经过 d，d 支配 v。直接支配者形成树；对象 retained bytes 是其支配子树的 self_size 之和，包含自身。等价校验是删去对象后，原本根可达、现在不可达的总字节数。

例如 root → A、root → B、A → shared、B → shared。A/B 都不单独支配 shared，不能各自重复计入 shared。支配树图只分配当前对象的直接子树、自身以及未显示子树，面积不会把祖先与后代重复相加。

实现使用非递归 DFS 与 Cooper–Harvey–Kennedy 迭代，支配树直接子节点采用连续索引。算法预算超限会报错，不返回部分估算；预算限制工作量，不限制实际时间。不可达对象仍计入浅体积，但 retained bytes、根距离和支配者为空。根没有直接支配者。

多路径搜索逆向枚举不重复经过同一节点的根路径。complete 表示是否穷尽，stop_reason 说明路径条数、深度、边遍历或队列预算中止。预算计数是检查的入边数，不包括路径复制等所有 CPU/内存成本。循环组通过非递归强连通分量计算，也包含强自引用；循环存在本身不证明泄漏。

## 分配来源

可选 locations、trace_function_infos、trace_tree 和节点 trace_node_id 被索引。调用栈从分配位置到调用者排列。对象位置 line/column 为 0-based；分配函数信息保留 V8 原始坐标（通常 1-based，0/负数可表示未知）。分配位置的 count/size 描述整个位置的记录，不是单个对象当前大小。

只有采集时启用分配跟踪，才可能有对象分配栈；开启跟踪也不保证每个对象都有记录。GC、V8 版本和采集时机都可能影响记录。同一 ID 在清理快照的 trace_node_id 可以变为 0，而增长快照仍有记录。来源回查只在明确确认同 isolate 后进行，并校验 ID 的类型/名称；结果注明取自哪个快照。缺失信息不从名称推测。

## 聚合、比较与三阶段存活

浅体积是 V8 报告的 self_size，不是 RSS、全部 native memory 或浏览器总占用。分组键是 (kind, name)，不是类加载器、Realm 或模块身份。分组相加 shallow bytes，只提供最大单对象 retained bytes，绝不相加任意重叠支配子树。

aggregate 比较可用于不同进程，只提供分组数量和浅体积。identity_asserted 依赖使用者确认同一 V8 isolate 的快照序列；一个进程也可能有多个 isolate。同文件名、Node 版本或相似 ID 均不能证实来源。矛盾的同 ID 类型/名称会被拒绝，但没有矛盾不等于来源证明。

真实跟踪采集中观察到过少量同 ID 的类型/名称矛盾。默认严格拒绝；确认来源后可显式选择 exclude_conflicts，将任意快照对中有矛盾的 ID 从身份计数、变化和候选中排除，并返回排除数量及样本。聚合统计始终保留全部对象。网页会显示警示，报告会保留排除信息；被排除对象不能判断为新增、释放或存活。这个选项不把未知来源变成可信来源。

对象变化包括 added、removed、resized、retention_changed。容器自身没有变大，但保留的子树变大，仍会出现在保留变化中。变得可达或不可达也可报告，但 retained_delta 可能为空。新增/删除的 delta 以存在一侧的正/负 retained 为参照；所有对象的 delta 不能求和作为整个堆的增量。

三阶段候选定义严格为：A 中不存在，B 中存在，C 中仍存在。同 isolate 模式才按 ID 筛选；聚合模式只展示数量。候选既可能是意外持有，也可能是预热、正常缓存或运行时活动。即使某组数量回到基线，旧对象被释放、新对象仍存活，也不能把整组视作已完全清理。调查仍须检查根路径、分配位置和业务预期。

## 通用采集的来源记录

capture 在一个专用 Node 主 isolate 中运行场景模块。setup 后采集 A，反复 exercise 后采集 B，cleanup 后采集 C；每次采集前让出事件循环并请求 GC。GC 不能代替场景本身完成异步释放，cleanup 应等待需要的清理步骤。Worker 自身 isolate 的堆不自动包含在主 isolate 快照中。

capture.json 记录运行 ID、Node/V8 版本、场景、分配跟踪选项、文件大小及 SHA-256。--manifest 检查文件与清单相符后启用身份比较。清单不是签名证书，无法证明不可信第三方提供的快照来源；它保证记录与文件的一致性。报告保留来源参数以便复查。

## 资源边界

| 限制 | 默认值 |
|---|---|
| CLI/网页输入文件 | 每份 256 MiB，可调 1–1024 MiB；最多同时 3 份 |
| 库 max_chars | 268435456 个 String.length() 单位 |
| 节点 / 边 / 字符串条目 | 2000000 / 16000000 / 4000000 |
| 支配计算工作预算 | 200000000 |
| 单个字符串或元数据值 | 16 Mi 字符，元数据嵌套深度 64 |
| 最短根路径 | 1024 条边，可调整 |
| 多路径搜索 | 5 条、深度 64、检查 50000 条入边、待处理分支最多 4096 |
| 结果分页 | 1–1000 条 |
| 完整报告 | 每个列表默认 100000 行，可调至 1000000；文本最多 64 Mi 字符 |

节点/边数在记录组装时完整验证；增量阶段的数值记录另有粗粒度上限，以允许元数据位于数组之后。Limits 和这些预算不是内存硬配额。图与索引依然整体驻留内存，逐块解析避免全文件文本与大型 JSON 数值树同时驻留，不是磁盘外存图算法。三份快照及报告临时数据会明显增加占用。

65 MiB 输入测试验证跨越旧大小上限与持续逐块读取，不代表在任意机器保证处理 1 GiB 复杂快照。规模测量见 [VALIDATION.md](VALIDATION.md)。

CLI/网页严格解码 UTF-8，并校验实际字节数。网页取消会终止 Worker、清空当前三份快照；已保存的 IndexedDB 工作区不受影响，可以重新导入。CLI 可由调用者终止进程。

CLI 与网页都调用同一编译后的 MoonBit 内核。宿主只负责文件、采集进程、交互和显示；测试中的独立算法只用于核对。
