支配者树是基于GC Roots可达路径推导出的逻辑结构:若A是B的支配者,则所有从GC Roots到B的路径必经A,A不释放则B无法回收;它揭示“谁卡住内存释放”,而非“谁占内存最多”,Retained Size高的支配者常是内存泄漏总开关。

支配者树(Dominator Tree)不是真实存在的结构,而是根据对象引用路径推导出的逻辑关系:如果对象 A 是 B 的支配者,那么所有从 GC Roots 到 B 的路径都必须经过 A;A 不释放,B 就无法被回收。它不反映“谁占内存最多”,而是揭示“谁卡住了内存释放”——Retained Size 高的支配者节点,往往就是内存泄漏的总开关。
看 Dominators 视图要抓重点指标
在 Chrome DevTools Memory 面板拍摄堆快照后,切换到 Dominators 标签页:
- 按 Retained Size 降序排列,优先关注占比超过 10% 的前几行
- 重点关注构造函数为
(closure)、Object、Array或自定义类名(如UserCache、DataTable)的条目 - 留意 Retained Size 明显大于 Shallow Size 的对象——说明它自身很小,却拖着大量子对象
- 右键点击某行 → Reveal in Summary view,再点 Constructor 查同类实例总数,确认是否批量泄漏
避开常见误判陷阱
支配者树容易把“中间人”当成元凶,需进一步验证:
-
window或globalThis常是顶层支配者,但它只是入口;真正问题通常藏在它的某个属性里(比如未清理的window.cacheMap) -
(closure)频繁上榜,但要点开看它捕获了哪个外层变量——若捕获的是已卸载组件的this或大数组,才是真凶;若只是几个字符串或数字,可忽略 - 第三方库对象(如
lodash.throttle返回的函数、React.Component实例)可能成为支配者,但根源常在调用方没执行cleanup或unmount - DOM 节点显示为 Detached DOM tree 的支配者,说明已被移除但仍有 JS 引用(如事件监听器、缓存 map),此时应检查其 Retaining tree 中是否有
EventListener或data属性
Node.js 环境下必须注意的细节
用 --inspect 连接 Chrome DevTools 分析 Node.js 时,支配者逻辑一致,但有硬性前提:
- 启动时必须加
--expose-gc参数 - 拍快照前手动调用
global.gc(),否则大量本可回收的对象仍处于“待回收”状态,支配者树会失真 - 建议搭配
--max-old-space-size限制老生代大小,让泄漏更快暴露
结合 Retainers 树交叉验证
Dominators 告诉你“谁占着内存不放”,Retainers 才告诉你“谁不让它走”。发现可疑支配者后,立刻右键 → Reveal in Retainers:
- 看引用链终点是不是长期存活对象(如
window、globalThis、模块级缓存 Map) - 重点排查
addEventListener、setTimeout、URL.createObjectURL、document.querySelector等典型泄漏模式 - 若 Retainers 路径中出现
(system)或空白,说明引用来自 V8 内部机制(如 Promise 微任务队列),需检查异步逻辑是否遗漏 resolve/reject

















