前端面试 P037:内存泄漏的快照与引用链排查
Showing the original in Chinese (China)
来源:方格前端讲面试 · 37.JS 内存泄漏场景与排查,8 分 47 秒。
回看定位:02:45 示例保留路径 · 03:36 快照基线 · 07:10 Retainers
泄漏要看不再需要却仍可达的数据
课程示例把生成的 DOM 节点保存到全局 leaked 数组,节点又带有引用大对象的监听器。清空页面 DOM 后,全局数组仍保留节点,相关数据因此继续可达。
关键不是“DOM 从页面消失”或“有闭包”,而是仍有哪条从根到对象的保留路径。对象之间形成循环引用,也不意味着现代可达性垃圾回收器一定无法回收。
三次快照如何安排
- 页面稳定后记录基线,必要时先预热,避免把首次初始化都当成泄漏。
- 执行可重复的创建、挂载或打开操作,记录增长后的快照。
- 执行关闭、卸载、清理,等待相关任务结束并在合适条件下回收,再记录快照;比较仍持续保留的对象。
在 Comparison 视图看数量、大小增量,找到可疑 Detached 节点或大对象,然后查看 Retainers,定位由哪份全局集合、监听器、定时器或缓存保留。
如何修复示例
业务停止后移除长期容器中的对象引用;对挂在长期存活目标上的监听器解绑,或使用 AbortSignal 统一清理;取消定时任务,清理不再使用的缓存。
只对节点执行 remove 或清空 innerHTML,不会顺便清空你另外保存的数组。反过来,节点和其监听器若整体不再可达,也不必因为“存在事件监听”就断言必然泄漏。
需要补充的证据边界
刷新会重建大量页面状态,内存大幅下降本身不是泄漏证明。单次内存峰值可能来自正常工作集、尚未触发 GC 或工具自身开销;应看多轮操作后清理阶段是否持续增长。
Detached 也可能是预期缓存。结合实际用途、Retainers 和复现验证判断,不仅看名称。控制台历史、选中的 DOM 和调试变量也可能人为保留对象。
修复后重复同一流程,确认不必要的保留路径消失,避免只凭总数字变小就结束。