console导致
console导致的内存泄漏 因为打印后的对象需要支持在控制台上查看,所以传递给console.log方法的对象是不能被垃圾回收的。我们需要避免在生产环境用console打印对象。
定时器
在组件初始化的时候设置了setInterval,那么在组件销毁之前记得调用clearInterval方法取消定时器。
没有正确移除事件监听器(各种EventBus, dom事件监听等)。
特征:performance里,监听器数量会持续上升。
尽管大部分同学都会有主动移除监听器的观念,但如果姿势不对,可能依旧会造成内存泄漏。下面是一个真实案例:
// 版本一
mounted() {
window.addEventListener('resize', debounce(this.handleWidthChange, 100))
},
beforeDestroy() {
window.removeEventListener('resize', debounce(this.handleWidthChange, 100))
}
乍一看好像写的还不错,有及时移除监听器,对resize这种频繁触发的事件也加了debounce处理。但其实这段代码就导致了内存泄漏:每次调用debounce(this.handleWidthChange, 100)
时, 其实都会返回一个新的函数,导致addEventListener
和 removeEventListener
方法传入的回调函数已经不是同一个回调函数,监听器没有被正确移除,内存泄漏。
修改后的代码一:
// 版本二
data() {
return {
debounceWidthChange: null
}
},
mounted() {
this.debounceWidthChange = debounce(this.handleWidthChange, 100)
window.addEventListener('resize', this.debounceWidthChange)
},
beforeDestroy() {
window.removeEventListener('resize', this.debounceWidthChange)
}
修改后,监听和移除监听的已经是同一个回调函数了。
结束
简单总结一下排查内存泄漏的常见流程:
- 用
performance
面板记录操作一段时间内的内存变化,找出可能发生内存泄漏的操作。 - 用 three snapshot 技巧,记录下发生泄漏前后的内存快照
- 用
comparison
视图对泄漏前后的内存快照进行比较,找出泄漏的对象。 - 重点关注
Vue Component
,Detached HTMLDivElement
等Constructor
。
“three snapshot”
技巧:
- 打开
DevTools
, 切换至![请添加图片描述](https://img-blog.csdnimg.cn/77ef223129f94900970262a52a05320b.webp)
面板 - 先记录一个堆内存快照
- 在你的页面上执行可能发生泄漏的操作
- 再记录一个堆内存快照
- 多重复执行几遍步骤3
- 最后记录一个堆内存快照
- 选择最后一个堆内存快照,找到顶栏的“All objects”, 切换至”Objects allocated between snapshots 1 and 2”(也可以对2,3执行同样的操作)
- 切换后,你就能看到两个快照之间新生成的对象。你可以选择其中一项点开,看看它的
retaining tree
里面保留了哪些对象没有释放。
Tips:在记录第一个堆快照之前你可以先做一些“预热”操作,避免一些懒加载和缓存策略影响到了对内存的分析。