常见的内存泄漏场景

console导致

console导致的内存泄漏 因为打印后的对象需要支持在控制台上查看,所以传递给console.log方法的对象是不能被垃圾回收的。我们需要避免在生产环境用console打印对象。

定时器

在组件初始化的时候设置了setInterval,那么在组件销毁之前记得调用clearInterval方法取消定时器。
没有正确移除事件监听器(各种EventBus, dom事件监听等)。
特征:performance里,监听器数量会持续上升。
常见的内存泄漏场景_第1张图片
尽管大部分同学都会有主动移除监听器的观念,但如果姿势不对,可能依旧会造成内存泄漏。下面是一个真实案例:

// 版本一
mounted() {
    window.addEventListener('resize', debounce(this.handleWidthChange, 100))
},
beforeDestroy() {
    window.removeEventListener('resize', debounce(this.handleWidthChange, 100)) 
}

乍一看好像写的还不错,有及时移除监听器,对resize这种频繁触发的事件也加了debounce处理。但其实这段代码就导致了内存泄漏:每次调用debounce(this.handleWidthChange, 100)时, 其实都会返回一个新的函数,导致addEventListenerremoveEventListener方法传入的回调函数已经不是同一个回调函数,监听器没有被正确移除,内存泄漏。
修改后的代码一:

// 版本二
data() {
    return {
        debounceWidthChange: null
    }
},
mounted() {
    this.debounceWidthChange = debounce(this.handleWidthChange, 100)
    window.addEventListener('resize', this.debounceWidthChange)
},
beforeDestroy() {
    window.removeEventListener('resize', this.debounceWidthChange)  
}

修改后,监听和移除监听的已经是同一个回调函数了。

结束

简单总结一下排查内存泄漏的常见流程:

  1. performance面板记录操作一段时间内的内存变化,找出可能发生内存泄漏的操作。
  2. 用 three snapshot 技巧,记录下发生泄漏前后的内存快照
  3. comparison视图对泄漏前后的内存快照进行比较,找出泄漏的对象。
  4. 重点关注 Vue Component, Detached HTMLDivElementConstructor

“three snapshot”技巧:

  1. 打开DevTools, 切换至![请添加图片描述](https://img-blog.csdnimg.cn/77ef223129f94900970262a52a05320b.webp) 面板
  2. 先记录一个堆内存快照
  3. 在你的页面上执行可能发生泄漏的操作
  4. 再记录一个堆内存快照
  5. 多重复执行几遍步骤3
  6. 最后记录一个堆内存快照
  7. 选择最后一个堆内存快照,找到顶栏的“All objects”, 切换至”Objects allocated between snapshots 1 and 2”(也可以对2,3执行同样的操作)
    常见的内存泄漏场景_第2张图片
  8. 切换后,你就能看到两个快照之间新生成的对象。你可以选择其中一项点开,看看它的retaining tree里面保留了哪些对象没有释放。

Tips:在记录第一个堆快照之前你可以先做一些“预热”操作,避免一些懒加载和缓存策略影响到了对内存的分析。

你可能感兴趣的:(开发工具,javascript,开发语言,性能优化)