排查内存泄漏需快照与断点闭环:先拍三张内存快照比对定位泄漏对象及保留路径,再通过retainers追溯引用链,在源码可疑处设断点验证未释放原因。

排查复杂隐蔽 Bug,关键不是单靠快照或断点,而是让两者形成闭环:用内存快照定位“谁没被回收”,再用断点验证“为什么没被回收”。真正卡住的往往不是现象,而是引用链中某个看似无害的中间节点——比如一个闭包变量、一个未清除的定时器回调、或一个被 jQuery.data 持有的 DOM 节点。
先拍快照,锁定可疑对象和保留路径
打开 Chrome DevTools 的 Memory 面板,确保先点小垃圾箱图标手动触发 GC,再拍三张快照:
- 基线快照(页面空闲、操作前)
- 操作后快照(如打开又关闭弹窗、切换路由)
- 重复操作后快照(确认是否阶梯式增长)
切换到 Comparison 视图,以第一张为基准,重点关注 # New − # Deleted 明显为正的对象类型。点击构造函数(如 MyChart、Closure、HTMLDivElement),右键 → “Reveal in Summary view”,再选中具体实例 → 右侧面板切到 Retainers 标签。顶部几层就是强引用链:如果看到 window、setTimeout、EventListener 或某闭包(Closure)持续存在,就说明它本该随组件销毁却被意外持有了。
顺着 Retainers 找到源码位置,设断点验证行为
Retainers 列表里每个节点都带源码链接(如 myModule.js:142)。点击它,DevTools 自动跳转到对应行。这时不要只看那行代码,重点检查:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
- 该函数是否在组件卸载后仍被注册为事件监听器(比如绑在
document上但没removeEventListener) - 是否有
setInterval回调捕获了外部大对象(如闭包里存着整个数据列表),且未在清理阶段clearInterval - 是否用
Map或Set缓存了 DOM 元素,但没在元素移除时同步删除条目
在疑似泄漏源头打上断点,重新走一遍流程:打开 → 关闭 → 触发 GC → 看断点是否还命中。如果组件已销毁却仍进断点,就坐实了引用未断。
用条件断点+作用域检查,揪出隐性闭包持有
很多泄漏藏在闭包里。比如一个防抖函数返回的回调,内部闭包长期锁住了原始参数对象。这时可在疑似闭包函数入口设条件断点,例如:
`this === window || arguments[0]?.id === 'target-item'`断点命中后,展开右侧 Scope 面板,逐个查看 Closure 下挂载的变量。如果发现某个本该释放的大数组、完整响应数据或 DOM 节点依然存在,说明这个闭包就是泄漏锚点。接着顺藤摸瓜,查是谁创建了它、谁保存了它、谁忘了清理它。
结合 Allocation Timeline 定位高频分配源头
如果快照对比看不出明显新增对象,但内存曲线持续爬升,启用 Allocation instrumentation on timeline 更有效。录制操作过程,停止后点高频构造函数(如 Object、Array),勾选实例 → 查 Retainers。若发现大量实例都被同一个闭包或定时器持有,就说明不是对象创建太多,而是它们被统一“扣押”在某处没放行。这时候回到源码,在那个持有者附近加断点,观察它生命周期是否与业务逻辑错配。










