排查闭包泄漏需聚焦“不该活却活着的闭包”,通过两次精准gc后对比快照,在comparison中筛选closure,关注new>0、retained size异常及高频泄漏函数名,再沿retainers定位根因对象及captured values中的大内存引用。

排查闭包引起的隐蔽泄漏,关键不是看“有没有闭包”,而是确认“哪个闭包不该活却一直活着,还拖着大对象”。这类泄漏往往不报错、不卡顿,只在反复操作后内存阶梯式上涨,容易被忽略。
拍两张干净快照,时间点必须卡准
很多误判源于快照干扰太多。真正有效的对比,需要严格控制 GC 时机:
- 打开 Memory 面板,先点垃圾桶图标手动触发垃圾回收(GC)
- 拍下第一张快照(Baseline),此时应尽量“空”
- 执行疑似泄漏的操作(比如打开/关闭弹窗 3 次、切换路由、刷新数据列表)
- 主动清理:调用 dispose()、removeEventListener()、把变量设为 null
- 再次 GC,立刻拍第二张快照
在 Comparison 视图里盯住 Closure 的异常信号
选中第二张快照 → 右上角切为 Comparison 模式 → Constructor 列筛选 “Closure”。重点关注三类线索:
- # New > 0:说明新闭包不断生成,旧的却没释放
- Retained Size 达几百 KB 或 MB 级:闭包本身极小,这么高说明它锁住了 DOM 节点、大型数组或组件实例
- 函数名含 debounce、onResize、uploadHandler、handleClick 等:这些是高频泄漏位点,尤其在未解绑场景下
顺着 Retainers 找到真正“不放手”的那个东西
双击一个可疑 Closure → 右侧点 Retainers 标签 → 从上往下看引用链:
- 第一层常是 function 或 timer,别停在这儿
- 继续往下找第一个你写的对象,比如 MyModal、window.cache、setInterval 回调、Map.entries
- 右键它 → “Reveal in Summary view”,看它的类型和 Retained Size
- 如果终点是已移除的 HTMLDivElement、全局 window.handler、未清除的 setInterval,就是泄漏根因
展开 Captured Values,看清它到底拖住了什么
回到 Closure 实例 → 展开 Properties → 找 “Captured values” 区域:
- 留意变量名含 data、list、state、ref、cache、buffer 等关键词
- 点开其中一项,看它是 Uint8Array、VueComponent 还是百万级 Object
- 如果该值 Retained Size 很高,且对应组件早已卸载或 DOM 已 detached,这就是被闭包拖住的“真凶”
不复杂但容易忽略











