blog icon indicating copy to clipboard operation
blog copied to clipboard

Vue 应用的内存泄露分析

Open ryancui92 opened this issue 5 years ago • 0 comments

最近在做一个基于 Vue + ECharts 的大屏数据可视化应用时,发现在轮播时有大量的内存泄漏现象。单凭直觉与日常开发中看到的代码,考虑是以下几个原因:

  • ECharts 库 canvas 本身的内存泄漏
  • Vue 里各种监听器的没有及时销毁

因此在使用 devtool 的 Memory 定位问题时,亦沿着这个方向进行排查。

Chrome devtool Memory Tab 的用法大家自行 Google

对页面做一次 Memory Snapshot, 然后随意做一下轮播,再做一次 Snapshot 并切换到 Comparison 第一次快照。

image

按 Allocate Size 做个降序,可以看到 (array), (closure) 这些项排在了最前面。查看一下里面的内容。

没有及时销毁的监听器

image

点开其中一个 array, 发现里面有 V8EventListener/EventListener 的标记,而在代码里全局搜一下下面的事件名,便发现:

Vue.nextTick(() => {
  window.addEventListener('resize', this._resizeEventHandler, false);
  this._initOption();
})

在 Vue 实例中不停地添加监听器,但没有在销毁组件时及时销毁对应的监听器。

顺着这个思路可以把所有监听器导致的内存泄露都排查一遍。包括 window.addEventListener, Vue 的 bus.$on 之类的。

Vue 中过多的 Observer

在 Memory Tab 中还能看到一大堆这样的 get/set closure

image

这是 Vue 对对象属性做 Reactive getter 和 setter 的函数,打开里面一看,全是 echarts 的实例里的属性。此时我们发现,就是 Vue 把 echart instance 里的属性全部监听了:

new Vue({
  data: {
    instance: null // 这里
  }
})

但由于我们使用 echart 并不需要用到响应式的功能,因此我们把它从 data 中去掉,直接在 created 钩子中挂载 instance 变量就 ok 了。

created() {
  this.instance = null
}

ryancui92 avatar Apr 04 '19 02:04 ryancui92