Chrome DevTools 中追踪闭包泄漏需用 Memory 面板的 Heap Snapshot,配合 Allocation instrumentation on timeline 和 Retainers 分析:先手动 GC 后操作再清理,二次 GC 后拍快照;在 Summary 中按 Retained Size 查 (closure) 对象,展开 [[Scopes]] 找保留变量;通过 Retainers 向上追溯至 window、定时器或事件监听器等根因;若疑高频创建,改用 Performance 面板启用 Allocation instrumentation on timeline 查 Call Tree。

Chrome DevTools 并没有叫“内存记录器”的独立面板,你实际想用的是 Memory 面板中的 Heap Snapshot(堆快照),配合 Allocation instrumentation on timeline(分配插装) 和 Retainers 分析 来追踪闭包引起的非预期保留路径。关键不在于“录”,而在于“拍得准、看得清、追得深”。
先拍一张干净的快照
闭包泄漏往往藏在“清理后本该消失却还在”的对象里:
- 打开 Memory 面板 → 点击左上角垃圾桶图标,手动触发一次 GC
- 执行你要测试的操作(比如打开弹窗、初始化图表),再主动清理:调用 dispose()、removeEventListener()、清空变量引用(如设为 null)
- 再次点击垃圾桶 → 立即拍下 Heap Snapshot。这张快照里如果还有大量闭包残留,就是泄漏信号
在快照中定位可疑闭包
切换到 Summary 视图,按 Retained Size 降序排列,重点关注:
- 类型为 (closure) 的对象,Retained Size 达几百 KB 甚至 MB 级
- 构造函数列为 Closure,但右侧 Size 很小、Retained Size 却异常大——说明它背后绑着大东西
- 点开后看 Properties → 展开 [[Scopes]] → 找到 Closure scope → 这里面列出的变量(如
data、config、ref)才是真正的保留源
顺着 Retainers 追到真正的“钉子”
闭包本身不是根因,它只是中间一环。真正让它活下来的是上层长期存活的对象:
- 选中闭包 → 右侧切到 Retainers 标签 → 逐层向上点,直到看到终点是:window.xxx、globalThis.map、setInterval、document.addEventListener 或某个第三方库实例(如
EventBus、WebSocket) - 若某一层是
bound listener或timer,右键 → Reveal in Summary view,跳过去查它的 Retained Size 和引用链 - 特别注意那些名字像
uploadHandler、onResize、debouncedSearch的闭包——它们常因未解绑或未清理而长期驻留
用分配插装确认高频创建源头
如果怀疑是“反复新建闭包”而非“闭包没释放”,就启用更轻量的实时追踪:
- 切到 Performance 面板 → ⋯ → 勾选 Allocation instrumentation on timeline(注意不是 Memory 面板里的同名选项)
- 录制并复现操作 → 查看底部绿色 Allocations 轨道
- 在 CPU 尖峰处点击绿色条块 → 看 Call Tree 中顶部是否是 Closure 构造函数,调用栈是否指向
render、forEach或useEffect——这说明你在循环或渲染中不断生成新闭包

















