支配者树是基于引用路径推导的逻辑关系,用于定位卡住内存释放的关键节点;通过dominators视图分析retained size占比、构造函数名及与shallow size差距,结合retaining tree追溯业务对象和闭包捕获内容,并辅以多快照对比验证泄漏源头。

支配者树不是真实存在的结构,而是根据引用路径推导出的逻辑关系:如果对象 A 是 B 的支配者,意味着所有从 GC Roots 到 B 的路径都必须经过 A;A 不释放,B 就无法被回收。它不告诉你“谁泄漏了”,但能快速指出“谁卡住了最多内存释放”——这正是定位单页应用内存峰值异常的突破口。
看 Dominators 视图时盯紧三类关键指标
打开 Chrome DevTools → Memory 面板 → 拍摄堆快照 → 切换到 Dominators 标签页:
- Retained Size 占比 >10% 的前几行优先排查,尤其是排在最顶部的 2–3 个节点
- 关注构造函数名:(closure)、Object、Array、自定义类(如 ChartManager、UserCache)或第三方库类(如 LodashWrapper、ReactComponent)
- 对比 Retained Size 和 Shallow Size 差距:若 Retained Size 远大于 Shallow Size(比如自身仅占 2KB,却拖着 80MB),说明它像“根节点”一样锁住了一整棵子树
从支配者出发,顺藤摸瓜查真正元凶
支配者常是“中间人”,不是真问题。双击某行进入右侧 Retaining Tree(保留树)后,重点做这几件事:
- 展开路径,找第一个非系统级对象:跳过 window、globalThis、system 等顶层入口,往下看到第一个业务相关变量(如
window.appState、document.__cache、myRouter.historyStack) - 右键该对象 → Reveal in Summary view → 切回 Summary 标签页,按 Constructor 过滤同类实例数:若
MyDataTable实例有 47 个(而预期仅 1 个),就是批量泄漏信号 - 留意 (closure) 节点捕获的内容:点开 closure → 查看 “Scope” 或 “Context” 中保存的变量,确认是否意外持有了已卸载组件的
this、大数组或整个响应体数据
避开常见误判陷阱
很多“高 Retained Size”的支配者只是表象,需结合上下文排除干扰:
-
window 几乎总是顶层支配者,但它只是环境入口;真正问题通常藏在它的某个属性里,比如未清理的
window.tempUploads或window.eventBus.subscribers -
Detached DOM tree 类型支配者,说明 DOM 节点已被移除,但仍有 JS 引用(如事件监听器、Vue 绑定、jQuery.data 缓存),此时要检查 Retaining Tree 中是否有 EventListener、
__vue__、data-v-xxx等线索 -
第三方库对象(如 React.memo 返回的组件、Axios 实例、Map 构造的缓存)频繁上榜,根源往往不在库本身,而在调用方没执行
cleanup、unmount或clear()
配合对比快照验证是否真为峰值源头
单张快照容易误判。建议在关键操作前后连续拍摄三张:
- 空闲态(Baseline)→ 执行一次路由跳转/模态框打开关闭 → 再次空闲态
- 切换到 Comparison 视图,筛选 Constructor 为可疑类名(如 (closure)、MyChartPlugin),观察 Delta 列:若 “# New” 显著增加且 “# Deleted” 为 0,基本锁定泄漏源头
- 再回到 Dominators,只看这三张快照中始终稳定占据 Top 3 的节点——它们大概率就是导致内存持续攀升的“总开关”










