快照对比需先手动触发GC再拍快照,按Retained Size排序排查持续增长对象,通过Retaining Tree定位强引用源头,并结合Allocation Timeline锁定分配代码。

快照对比不是简单地“拍两张图看谁变多了”,而是通过控制变量、聚焦引用关系、识别异常增长模式,把泄漏对象从海量内存数据中揪出来。关键在操作顺序、观察维度和归因逻辑。
必须先手动触发 GC 再拍快照
Chrome Memory 面板里那个小垃圾箱图标(Collect garbage)不是摆设。不点它就直接拍快照,堆里会混着大量本该被回收但还没来得及清理的临时对象,干扰判断。正确流程是:
- 执行一次稳定操作(比如打开一个弹窗、加载一组列表)
- 点击垃圾箱图标强制 GC
- 立即拍第一张快照(baseline)
- 重复相同操作几次(模拟用户行为)
- 再次 GC → 拍第二张快照
- 必要时再做一轮 → 拍第三张
重点看 Retained Size 和增长对象类型
在快照对比视图(Comparison)中,不要只盯着“新增了哪些构造函数”,要按 Retained Size 降序排列,优先排查那些数值大、且在多张快照中持续增长的对象。常见高危类型包括:
- (closure):闭包本身不危险,但若它长期持有大数组、缓存对象或 DOM 节点,Retained Size 就会异常高
- Detached HTMLDivElement / Detached DOM:表示 DOM 已从文档树移除,但 JS 仍持有引用(如未清空的变量、事件监听器绑定在已卸载组件上)
- Array、Object、Map、Set:如果它们的 Retained Size 明显大于 Shallow Size,说明背后挂了一长串不可回收的引用链
- EventListener:尤其关注没被 removeEventListener 解绑的监听器,常与 detached DOM 共存
顺着 Retaining Tree 找到“不该存在的强引用”
选中一个可疑对象,展开它的 Retaining Tree(保留树),看是谁在“拽着它不让走”。真正泄漏的根源,往往藏在看似合理的引用路径末端。例如:
立即学习“Java免费学习笔记(深入)”;
- 路径显示
window → myCache → largeDataArray,而myCache本应在页面离开时清空,却一直存活 → 检查销毁逻辑是否遗漏 - 路径是
timer → closure → componentInstance → state,但组件已卸载 → 定时器未 clearTimeout/clearInterval - 出现
global → unDeclaredVar:说明有未声明变量自动挂到 window 上,属于隐式全局变量泄漏
配合 Allocation Timeline 锁定分配源头
如果快照对比看到某类对象持续增长,但不确定哪段代码创建了它们,就切到 Allocation Timeline 面板:
- 开启录制,执行疑似泄漏的操作
- 停止后,按 Constructor 筛选目标类型(如 HTMLDivElement)
- 点击某次分配条目,DevTools 会高亮对应源码位置(需 sourcemap 支持)
- 结合调用栈,确认是不是在组件 mount 后反复 new 实例,却没在 unmount 时释放
不复杂但容易忽略


















