chrome devtools memory面板排查闭包内存泄漏需拍两次堆快照比对:先手动gc后拍baseline,执行操作并清理后再gc拍第二张;在comparison模式筛选(closure),关注#new与retained size异常增长项,通过retaining tree追溯持有者,并展开[[scopes]]查看实际捕获变量。

直接用 Chrome DevTools 的 Memory 面板拍堆快照比对,重点看 Closure 对象是否异常增长、Retained Size 是否偏高,并顺着 Retaining Tree 找出谁在长期持有它。
拍两张快照做对比
先手动触发一次垃圾回收(点 Memory 面板左上角垃圾桶图标),再拍第一张快照(Baseline);执行疑似泄漏的操作(比如打开/关闭弹窗、切换路由、刷新列表),再手动 GC 一次,拍第二张快照。在 Comparison 模式下筛选 (closure),重点关注 # New 和 Retained Size 明显上升的条目——不是数量多就一定有问题,而是每次操作后都稳定新增、且 Retained Size 达几百 KB 甚至 MB 级,才值得深挖。
顺着 Retaining Tree 找持有者
双击一个可疑的 Closure 条目,右侧展开 Retaining Tree。真正吃内存的不是闭包本身,而是它捕获的大对象(比如 DOM 节点、组件实例、百万级数组)。关键看引用链终点:
- 如果顶层是 window.myHandler 或 globalThis.xxx,说明被全局变量钉住了
- 如果是 EventListener 且对应 DOM 已 Detached,基本就是事件没解绑
- 出现 setInterval、Map 或某个长期存活的 class 实例,也都是典型泄漏源头
展开 [[Scopes]] 看实际捕获了什么
Retainers 只显示“谁在引用”,看不出闭包里到底锁住了哪些变量。正确做法是:在 Summary 视图选中该 Closure → 右侧 Properties 里找到 [[Scopes]] 并展开 → 进入类型为 Closure 的 scope → 查看里面的 Properties。这里会列出真实被捕获的变量名和值,比如 data、this.$el、chartInstance。如果只用 id 却捕获了整个用户对象,就是优化点。
用 Allocation Timeline 锁定创建位置
启用 Allocation instrumentation on timeline → 开始录制 → 执行一次操作 → 停止。时间轴上的蓝色块代表新分配且未回收的对象。点击任一蓝块,左侧堆栈会定位到生成闭包的具体代码行,比如某个 return () => { ... }。特别留意回调函数里是否无意引用了大数组、DOM 节点或响应式状态。











