chrome devtools需手动通过堆快照retainers链追踪内存泄漏:先拍空闲基线快照,再执行操作并清理后拍第二、三张,对比第三张与第一张,重点关注#delta持续上升的detached dom等对象,从detached节点点击进入retainers自下而上分析引用链。

Chrome DevTools 本身不自动标出“哪个引用导致泄漏”,而是通过堆快照(Heap Snapshot)中的 Retainers 引用链,让你手动逆向追踪——谁在拽着本该被释放的对象不放。
先拍干净的快照,再对比增长
单张快照没意义。必须至少拍三张:
- 空闲时点垃圾桶图标(Collect garbage),再拍第一张(Baseline)
- 执行一次疑似泄漏操作(如打开/关闭弹窗),主动清理后拍第二张
- 重复该操作几次,再拍第三张
选中第三张 → 切换 Comparison 模式 → 与第一张对比。重点看 #Delta 为正且持续上升 的构造器,比如 Detached HTMLDivElement、(closure)、Array 或你自定义的类名。
从 Detached DOM 入手,最直观
搜索 detached,找到分离但未释放的 DOM 节点。点击任一实例 → 右侧打开 Retainers 标签页。从下往上读这条引用链:
- 最底下是那个 detached div
- window、
document、全局变量(如window.cache)、长期存活的单例(如路由实例、store),基本就是泄漏源头
例如路径:setInterval → 闭包 → this.data → window,说明定时器没清除,且闭包里还绑着整个组件数据。
闭包泄漏要筛 (closure),别只看 Function
在 Comparison 视图筛选器输入 (closure)(括号+小写,V8 内部标记)。如果数量和 Retained Size 明显增长,就说明有闭包长期持有了不该持有的对象。
- 点开一个大 Retained Size 的 (closure) → 看 Retainers
- 若顶层是
EventListener,检查对应 DOM 是否已移除,removeEventListener是否调用、函数引用是否一致 - 若顶层是
Timeout或Interval,查有没有漏掉clearTimeout/clearInterval - 若顶层是某个 class 实例,说明该实例没销毁,或它的属性(如
this.handler)仍持有闭包
配合分配堆栈,定位代码行
在 Memory 面板右上角齿轮 → 勾选 Record allocation stack traces。再拍快照后,点击某类增长对象 → 右侧能看到 “Allocation stack” —— 直接显示这个对象是在哪一行 new 或 function 创建的,精准到源码位置。
- 对第三方库返回的实例(如图表、编辑器),特别留意是否调用了
.destroy() - 对 Map/Set 缓存,检查 key 是否为对象、是否做了失效清理
- 对 useRef / ref / 全局变量,确认是否在组件卸载或操作结束时置空











