有效堆快照对比需拍三张逻辑关联快照:基线(gc后)、单次操作后(gc后)、重复操作后(gc后),通过comparison与dominators视图分析Δ>0构造函数、retained size异常增长及detached对象,结合retainers定位强引用泄漏源。

抓取堆快照做前后对比,核心不是“多拍几张”,而是让每次快照都反映一个明确的内存状态节点:空闲、操作后、重复操作后。关键在控制变量——手动触发垃圾回收(GC)、严格限定操作范围、精准选择对比基准。
拍三张有逻辑关系的快照
别一上来就点“Take Snapshot”。真正有效的对比必须配合操作节奏和 GC:
- 刷新页面,等所有资源加载完成、无动画、无定时器运行;点击 Memory 面板左上角小垃圾桶图标(Collect garbage),再拍第一张——这是干净的基线快照(snapshot-0)
- 执行一次完整操作闭环(比如:打开模态框 → 点确定 → 关闭模态框),等待 2–3 秒确保异步逻辑结束,再点一次垃圾桶,拍第二张(snapshot-1)
- 重复同样操作 2–3 次,再次 GC,拍第三张(snapshot-2)。至少三轮,才能看出对象是否“只增不减”
在 Comparison 视图里盯住三类增长信号
选中 snapshot-2 → 右键 → Compare to previous snapshot,切换到 Comparison 列表,按 # Delta 或 Retained Size 排序,重点看:
-
Δ > 0 且持续上升的 Constructor:比如
Detached HTMLDivElement、(closure)、UserInfo、ChartInstance—— 这些不是“新增”,而是“该收没收” - Retained Size 明显增长但 Shallow Size 几乎不变:说明对象本身小,却拖着大片内存(典型如闭包捕获了整个组件实例或大数组)
-
Detached 对象反复出现且 Δ 稳定为正:不只是浏览器 DOM,Node.js 环境里的
Buffer、EventEmitter、pendingRequests也会被标为 Detached,它们往往是泄漏入口
顺着 Retainers 找到强引用源头
双击某条高 Δ 的构造函数(如 (closure) 或 Detached HTMLDivElement),右侧打开 Retainers 标签页,从下往上读引用链:
- 最底下是你选中的对象,往上是直接持有者,再往上是它的持有者……直到顶层 GC Root(如
window、document、定时器回调) - 重点不是链有多长,而是中间哪一环本该销毁却还活跃:比如已卸载的 React 组件仍被
eventBus回调持有;removeChild后的节点还被变量lastClickedNode引用;window.timerRef指向一个没clearTimeout的定时器 - 右键某行 → “Reveal in Summary view”,跳过去查同类对象总实例数,确认是否越积越多
必要时切 Dominators 视图看支配关系
回到 Memory 面板,左上角下拉菜单选 Dominators(不是 Summary 或 Containment):
- 它告诉你“谁一消失,下面一大片就能跟着回收”,默认按 Retained Size 降序排,前几行就是高风险目标
- 跳过
window、document这类顶层宿主,聚焦(closure)、Detached DOM或你项目里的类名(如ApiCache、ChartView) - 双击某行,右侧展开 Retaining Tree,看它具体支配了哪些对象(比如一个 Map 缓存 + 一堆 Detached 节点)
不复杂但容易忽略











