如何排查nuxt的内存泄露问题 & 优化

点击上方 前端Q,关注公众号

回复加群,加入前端Q技术交流群

背景

stalar电商平台是bigo 2020年的新业务,目标市场主要是中东五国,主要技术栈为nuxt。

一次常规需求上线后,偶然打开了chrome memory面板,打了几个内存快照,发现内存一直在涨,且无论跳转到什么页面,内存都稳定增长;为排除干扰因素,再快照前手动点击了gc,发现内存的增量仅仅下降了一点点,总体还是呈稳定增长趋势。意识到这是一个比较严重的问题,因为商详页面是有推荐商品模块的,也就是说用户的浏览路径在这里是没有尽头的,很有可能已经有用户出现在浏览大量商品后出现页面崩溃或者浏览器闪退的情况了(目前还缺乏页面崩溃监控,所以还不能确定)。

下图的内存快照,第一张是第一次进入商详页,第二张是在商详页中点击推荐商品进入下一张商详页,重复十次(下文比对内存等变化的截图全部采用这种方式)。两次生成快照前都手动点击了gc,可以看到内存张了12.3MB

原因排查

nuxt框架问题

观察发现任意页面的跳转,都会让内存稳定增长,即使是一些没有什么逻辑的简单页面,也有一定程度上的内存泄漏,所以首先怀疑nuxt框架或者依赖的其它轮子本身存在着内存泄漏的问题,google了一下发现nuxt的某些小版本确实存在内存泄漏问题,比如: nuxt/issue/7855

既然怀疑框架有问题,首先做的就是将nuxt升级到最新版本(其实我们用的nuxt版本已经比较新了,看nuxt的一些issue貌似是一些小版本有跳跃性的内存问题,比较迷惑),观察发现情况仅仅好转了一点,对于一些简单页面,内存已经不怎么增长了,但是重灾区商详页,还是能看到大幅度内存增长。

代码问题

排除掉框架的影响,回到chrome分析内存泄漏的原因,重新打开商详页并打开performance monitor,重复上文的从商详页点击推荐商品操作,发现JS heep sizeDOM Nodes、JSevent listenters这三项都在稳定增长,同样跳转10次,DOM Nodes从3k左右上涨到了11k,下图为跳转10次后的performance monitor面板截图:

同样是商详页,即使不同商品页面元素有差异,DOM Nodes也不可能有如此巨大的差异,event listenters也有稳定增长,所以怀疑是一些DOM的事件监听没有解绑,导致游离节点一直没有释放,再比较下上文打的两张内存快照,发现确实有非常大的detached node增长,印证了这个猜测。

先从全局方法入手。

一个封装的自定义指令,用作上报

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 monitorDOM NodesJS event listeners的数量都没有明显上涨了:

优化前

优化后

游离节点的Delta值(两张快照之间的差值)下降到了0!

优化前

优化后

最后看下内存快照的概览,发现内存已经没有上涨了

优化前

优化后

总结

内存泄漏的原因排查,学会使用chrome devtools工具十分重要,可以参考Chrome Tools,排查思路可以往这几个方面去考虑:

  • 全局变量

  • Dom脱离文档流仍被引用

  • 闭包

  • 第三方轮子未销毁以及重复创建

欢迎大家留言讨论,祝工作顺利、生活愉快!

关于本文

作者:bigo大魔王

https://juejin.cn/post/6926501702216450062


内推社群

我组建了一个氛围特别好的腾讯内推社群,如果你对加入腾讯感兴趣的话(后续有计划也可以),我们可以一起进行面试相关的答疑、聊聊面试的故事、并且在你准备好的时候随时帮你内推。下方加 winty 好友回复「面试」即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值