本文首发于:github.com/bigo-fronte… 欢迎关注、转载。javascript
stalar电商平台是bigo 2020年的新业务,目标市场主要是中东五国,主要技术栈为nuxt。前端
一次常规需求上线后,偶然打开了chrome memory面板,打了几个内存快照,发现内存一直在涨,且不管跳转到什么页面,内存都稳定增加;为排除干扰因素,再快照前手动点击了gc,发现内存的增量仅仅降低了一点点,整体仍是呈稳定增加趋势。意识到这是一个比较严重的问题,由于商详页面是有推荐商品模块的,也就是说用户的浏览路径在这里是没有尽头的,颇有可能已经有用户出如今浏览大量商品后出现页面崩溃或者浏览器闪退的状况了(目前还缺少页面崩溃监控,因此还不能肯定)。java
下图的内存快照,第一张是第一次进入商详页,第二张是在商详页中点击推荐商品进入下一张商详页,重复十次(下文比对内存等变化的截图所有采用这种方式)。 两次生成快照前都手动点击了gc,能够看到内存张了12.3MBnode
观察发现任意页面的跳转,都会让内存稳定增加,即便是一些没有什么逻辑的简单页面,也有必定程度上的内存泄漏,因此首先怀疑nuxt框架或者依赖的其它轮子自己存在着内存泄漏的问题,google了一下发现nuxt的某些小版本确实存在内存泄漏问题,好比: nuxt/issue/7855git
既然怀疑框架有问题,首先作的就是将nuxt升级到最新版本(其实咱们用的nuxt版本已经比较新了,看nuxt的一些issue貌似是一些小版本有跳跃性的内存问题,比较迷惑),观察发现状况仅仅好转了一点,对于一些简单页面,内存已经不怎么增加了,可是重灾区商详页,仍是能看到大幅度内存增加。github
排除掉框架的影响,回到chrome分析内存泄漏的缘由,从新打开商详页并打开performance monitor
,重复上文的从商详页点击推荐商品操做,发现JS heep size
、DOM Nodes、JS
、event listenters
这三项都在稳定增加,一样跳转10次,DOM Nodes
从3k左右上涨到了11k,下图为跳转10次后的performance monitor
面板截图:web
一样是商详页,即便不一样商品页面元素有差别,DOM Nodes
也不可能有如此巨大的差别,event listenters
也有稳定增加,因此怀疑是一些DOM
的事件监听没有解绑,致使游离节点一直没有释放,再比较下上文打的两张内存快照,发现确实有很是大的detached node
增加,印证了这个猜想。chrome
先从全局方法入手。浏览器
一个封装的自定义指令,用做上报markdown
V.directive('report', {
bind(el) {
if (option.onload) {
el.addEventListener('load', option.onload);
}
if (option.onerror) {
el.addEventListener('error', option.onerror);
}
}
});
复制代码
增长解绑方法后
V.directive('report', {
bind(el) {
if (option.onload) {
el.addEventListener('load', option.onload);
}
if (option.onerror) {
el.addEventListener('error', option.onerror);
}
},
unbind(el) {
if (option.onload) {
el.removeEventListener('load', option.onload);
}
if (option.onerror) {
el.removeEventListener('error', option.onerror);
}
}
});
复制代码
相似的还有对scroll监听的一些全局封装等等。
全局的方法扫了一遍后,发现状况好转的仍然很少,回到上文中打的两张内存快照,尝试从详情中找到产生内存泄漏的具体方法。
SkuBlock组件中监听了specsSChange:
代码为:
mounted() {
eventBus.$on('specsSChange', (specsS) => {
this.specsS = specsS;
});
}
复制代码
修改后:
mounted() {
eventBus.$on('specsSChange', (specsS) => {
this.specsS = specsS;
});
},
beforeDestroy() {
eventBus.$off('specsSChange');
}
复制代码
还有一些相似监听方法,修改方式类同,不一一举例说明。
使用一些第三方轮子,须要在组件中建立实例,若是在组件销毁后没有销毁轮子的实例,有可能会致使内存泄漏; 也能够经过内存快照详情,找到具体是哪一个组件中的轮子致使了内存泄漏。
例如商详页有一个复制分享连接的功能,使用了clipboard.js
,在商详页中是这样使用的:
mounted() {
const clipboard = new Clipboard('#copyLinkBtn');
clipboard.on('success', () => {
// do something
});
}
复制代码
我没有去细究clipboard.js
不销毁为何会引起内存泄漏,可是猜想是引用了DOM对象没有释放的缘由,修改方式也很简单,调用轮子提供的销毁方法便可
mounted() {
this.clipboard = new Clipboard('#copyLinkBtn');
this.clipboard.on('success', () => {
// do something
});
},
beforeDestroy() {
if (this.clipboard) {
this.clipboard.destroy();
}
}
复制代码
所有修改上线后,一样仍是用商详页点击推荐商品进入下一个商详页的方法,重复十次,来测试内存泄漏状况,首先观察performance monitor
,DOM Nodes
和JS event listeners
的数量都没有明显上涨了:
优化前
优化后
游离节点的Delta
值(两张快照之间的差值)降低到了0!
优化前
优化后
最后看下内存快照的概览,发现内存已经没有上涨了
优化前
优化后
内存泄漏的缘由排查,学会使用chrome devtools
工具十分重要,能够参考Chrome Tools,排查思路能够往这几个方面去考虑:
Dom
脱离文档流仍被引用欢迎你们留言讨论,祝工做顺利、生活愉快!
我是bigo前端,下期见。