支配树按“谁不释放谁就无法被回收”组织,A是B的支配者意味着所有根到B路径必经A;深堆即A释放后可回收的总内存;高深堆对象常驻于全局、静态字段、闭包或未清理监听器;Chrome中通过Dominators视图按Retained Size排序定位,并用多快照差分与引用链回溯确认泄漏。

理解支配树的关键逻辑
支配树不是按引用方向画的“谁引用了谁”,而是按“谁不释放,谁就无法被回收”的依赖关系组织的。一个对象A是对象B的支配者,意味着从根集(GC Roots)出发,所有到达B的路径都必须经过A。所以A一旦被释放,B及其整个子树都会被一并回收——这个子树总内存就是A的深堆(Retained Size)。
真正危险的巨型对象,往往不是自己体积最大,而是深堆极大:它像一根主干,拖着几十个、上百个子对象长期驻留。这类对象常出现在全局作用域、静态字段、闭包或未清理的事件监听器中。
在Chrome DevTools中定位高深堆支配者
打开堆快照后,默认视图是“Summary”或“Containment”。切换到Dominators标签页,这是专为识别支配关系设计的视图。这里每行代表一个支配者节点,右侧直接显示其深堆大小。
- 按“Retained Size”列降序排列,重点关注前10–20项
- 特别留意带有“(closure)”、“Window”、“Document”、“global”、“static”等标识的对象
- 展开某一行,查看其支配子树中是否包含大量本该随页面交互结束而销毁的组件实例、缓存数组、DOM节点等
交叉验证:用多快照差分锁定“本该释放却没释放”的证据
单次快照只能看到“此刻谁占得多”,但无法判断“它该不该还在”。必须配合操作流做对比:
- 快照1:页面加载完成、初始状态稳定后拍摄
- 快照2:执行一次模态框打开+关闭、或组件挂载+卸载后拍摄
- 快照3:重复上述操作2–3次后拍摄
在“Comparison”模式下,筛选出在快照3中Retained Size持续增长且未回落的支配者。如果某个闭包的深堆从2MB→5MB→12MB逐次上升,而它本应随组件销毁而消失,这就是典型的全局引用泄漏——比如事件监听器未解绑、React ref意外保留了DOM引用、或Vuex store中缓存了未清除的响应式对象。
结合引用链回溯“谁在死死拽着它”
选中可疑支配者,右键选择“Reveal in Dominators”或直接双击,在下方“Object Properties”面板中展开“References”或“Retaining Tree”。重点追踪以下几类强引用路径:
- Global Property:全局变量或window属性直接持有
- Closure:某个函数闭包通过自由变量捕获并长期持有了大数组、大Map等
- Listener:EventTarget上注册的监听器未调用removeEventListener
- Detached DOM tree:已从document移除但仍被JS对象引用的DOM节点集合
只要路径中出现一条非临时性、非预期性、生命周期远长于当前业务场景的引用,基本就能确认泄漏源头。

















