Skip to content

前端有哪些常见的内存泄漏? ​

泄漏是指这块内存已经用不到了,却还有引用把它留住,垃圾回收收不走。闭包本身不是泄漏。全局变量、没卸掉的监听、定时器、被缓存的 DOM,才是常见来源。

原理 ​

浏览器主堆上的对象,从根(全局、当前调用栈)出发走不到,就可以回收。意外的根会把整棵对象留住。

常见情况:

  • 意外的全局变量,或挂在 window 上的缓存从不删除。
  • setInterval、setTimeout 的回调还握着组件状态,组件却已经卸载。
  • DOM 事件监听没有 removeEventListener。委托到稳定的父节点,或在卸载时取消。
  • 脚本里保存了已经从文档移除的 DOM。元素上如果还有监听或数据属性,整棵子树都在。
  • 脱离文档的节点被 Map 用对象当键且用的是 Map 不是 WeakMap。WeakMap 的键是弱引用,键没了,记录可以一起走。

排查用开发者工具的 Memory 面板:拍两份堆快照,中间做一次「进入再离开」的操作,看离开之后哪些构造函数的数量没有掉下去。

追问 ​

闭包算泄漏吗?

闭包让外层变量活得和内部函数一样久。如果内部函数的寿命是你期望的,这是功能。如果回调忘了取消,闭包只是把不该留下的数据一起留住了。

SPA 为什么更容易漏?

路由切走时,页面 DOM 没了,组件里注册的全局监听、订阅、定时器不一定跟着销毁。在卸载钩子里成对清理。

易错点 ​

  • 把所有闭包都说成泄漏。
  • console.log 一个大对象时,开发者工具会保留它,快照看起来像泄漏。先关掉日志再测。
  • 移除 DOM 用 innerHTML = "" 时,如果别处还握着子节点引用,子节点不会消失。

相关 ​