Portable V8 heap snapshot graph analysis, retaining paths and explicit snapshot comparisons in MoonBit

| 调查步骤 | 功能 |
|---|---|
| 采集与导入 | 通用 Node 场景采集;分配跟踪;三份快照与 SHA-256 来源记录;真正逐块解析与读取进度 |
| 定位大对象 | 精确 UInt64 ID;名称、类型、大小及可达性筛选;浅体积和保留体积排序 |
| 追查引用 | 入边/出边、最短根路径、多条根路径、循环引用组及成员 |
| 分析持有关系 | 支配树导航、祖先路径、保留体积分布图;共享对象不重复加总 |
| 比较变化 | 聚合比较;同 isolate 对象匹配;自身大小及保留体积变化;两张表独立分页 |
| 清理后验证 | A 基线、B 增长、C 清理;筛出 A 不存在、B 出现、C 仍存在的对象 |
| 查找来源 | 对象源码位置、分配调用栈;经确认后可回查增长快照的分配记录 |
| 保存与交付 | 对象历史/书签、浏览器工作区保存恢复、全部匹配行的 JSON/CSV/HTML 报告 |
node scripts/serve.mjsgit clone https://github.com/white-bear233/MoonHeap.git
cd MoonHeap
npm run build
npm run demonode bin/moonheap.mjs capture --scenario examples/cache-scenario.mjs --output-dir work/my-capture --iterations 2 --allocation-traces --json
node bin/moonheap.mjs investigate work/my-capture/before.heapsnapshot work/my-capture/growth.heapsnapshot work/my-capture/cleanup.heapsnapshot --manifest work/my-capture/capture.json --exclude-conflicts --query MoonHeapCapturedEntry --kind object --jsonnode bin/moonheap.mjs summary examples/growth.heapsnapshot
node bin/moonheap.mjs nodes examples/growth.heapsnapshot --kind object --min-retained 32768 --limit 10
node bin/moonheap.mjs node examples/shared.heapsnapshot --id 9007199254740993
node bin/moonheap.mjs refs examples/growth.heapsnapshot --id 7
node bin/moonheap.mjs paths examples/shared.heapsnapshot --id 7 --max-paths 5 --json
node bin/moonheap.mjs tree examples/growth.heapsnapshot --id 7
node bin/moonheap.mjs cycles examples/growth.heapsnapshot
node bin/moonheap.mjs diff examples/before.heapsnapshot examples/growth.heapsnapshot --same-isolate --status retention_changed --json
node bin/moonheap.mjs report examples/growth.heapsnapshot --view nodes --format html --output work/objects.html
node bin/moonheap.mjs report examples/before.heapsnapshot examples/growth.heapsnapshot examples/residual.heapsnapshot --view investigate --same-isolate --format csv --output work/survivors.csvmoon add white-bear233/moonheap@0.2.0fn inspect_chunks(chunks : Array[String]) -> Unit raise @heap.HeapError {
let decoder = @heap.Decoder::new()
for chunk in chunks {
decoder.feed(chunk)
}
let snapshot = decoder.finish()
let analysis = snapshot.graph().analyze()
for node in analysis.nodes(kind="object", limit=10).items {
println("\{node.id} \{node.name} shallow=\{node.self_size}")
}
}moon test --target js --deny-warn
moon test --target wasm-gc --deny-warn
moon info
moon fmt
npm run build
npm test
node scripts/benchmark.mjs快照字节流 → MoonBit Decoder → 引用图 / 支配树 / 来源索引
→ 查询 / 比较 / 三阶段调查 / 报告
→ 同一 JS 桥接 → Node CLI / Web Workerpub(all) suberror HeapError {
Invalid(String)
Limit(String)
NotFound(String)
Identity(String)
} derive(Debug)pub(all) struct AllocationFrame {
function_id : String
name : String
script_name : String
script_id : Int
line : Int
column : Int
}type Analysisfn Analysis::compare(self : Analysis, after : Analysis, same_isolate? : Bool, exclude_conflicts? : Bool) -> RetentionDiff raise HeapErrortype Decodertype Graphfn Graph::root_paths(self : Graph, node : Int, max_paths? : Int, max_depth? : Int, max_work? : Int) -> PathSearch raise HeapErrorpub(all) struct Group {
kind : String
name : String
count : Int
shallow_bytes : Int64
max_retained_bytes : Int64?
}pub(all) struct GroupDelta {
kind : String
name : String
before_count : Int
after_count : Int
before_bytes : Int64
after_bytes : Int64
}type Inspectorpub(all) struct Investigation {
mode : String
groups : Array[ResidueGroup]
survivors : Array[Node]
created : Int?
released : Int?
excluded_ids : Array[String]
}pub(all) struct Limits {
max_chars : Int
max_nodes : Int
max_edges : Int
max_strings : Int
max_work : Int
} derive(Debug)pub(all) struct ObjectChange {
id : String
kind : String
name : String
status : String
before_bytes : Int64?
after_bytes : Int64?
}pub(all) struct ResidueGroup {
kind : String
name : String
before_count : Int
growth_count : Int
cleanup_count : Int
before_bytes : Int64
growth_bytes : Int64
cleanup_bytes : Int64
}pub(all) struct RetentionChange {
id : String
kind : String
name : String
status : String
before_bytes : Int64?
after_bytes : Int64?
before_retained : Int64?
after_retained : Int64?
retained_delta : Int64?
}type Snapshotfn Snapshot::allocation_stack(self : Snapshot, node : Int) -> Array[AllocationFrame] raise HeapErrorpub(all) struct SnapshotDiff {
mode : String
groups : Array[GroupDelta]
added : Int?
removed : Int?
survived : Int?
changes : Array[ObjectChange]
excluded_ids : Array[String]
}fn compare(before : Snapshot, after : Snapshot, same_isolate? : Bool, exclude_conflicts? : Bool) -> SnapshotDiff raise HeapErrorfn investigate(before : Analysis, growth : Analysis, cleanup : Analysis, same_isolate? : Bool, exclude_conflicts? : Bool) -> Investigation raise HeapErrorInstall
Download zipPortable V8 heap snapshot graph analysis, retaining paths and explicit snapshot comparisons in MoonBit