blog
blog copied to clipboard
Vue 应用的内存泄露分析
最近在做一个基于 Vue + ECharts 的大屏数据可视化应用时,发现在轮播时有大量的内存泄漏现象。单凭直觉与日常开发中看到的代码,考虑是以下几个原因:
- ECharts 库 canvas 本身的内存泄漏
- Vue 里各种监听器的没有及时销毁
因此在使用 devtool 的 Memory 定位问题时,亦沿着这个方向进行排查。
Chrome devtool Memory Tab 的用法大家自行 Google
对页面做一次 Memory Snapshot, 然后随意做一下轮播,再做一次 Snapshot 并切换到 Comparison 第一次快照。
按 Allocate Size 做个降序,可以看到 (array)
, (closure)
这些项排在了最前面。查看一下里面的内容。
没有及时销毁的监听器
点开其中一个 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
这是 Vue 对对象属性做 Reactive getter 和 setter 的函数,打开里面一看,全是 echarts 的实例里的属性。此时我们发现,就是 Vue 把 echart instance 里的属性全部监听了:
new Vue({
data: {
instance: null // 这里
}
})
但由于我们使用 echart 并不需要用到响应式的功能,因此我们把它从 data 中去掉,直接在 created
钩子中挂载 instance 变量就 ok 了。
created() {
this.instance = null
}