三行代码,修复 DOM 内存泄露
点击关注公众号,“技术干货”及时达!前言彦祖们,内存泄露应该是前端老生常谈的话题了
但是我们往往停留在理论层面,应付面试官,却十分缺少实战机会
本文是笔者在业务开发中遇到的一次真实案例,以此作为分享
技术栈:vue2(业务限制,未使用 keep-alive)
先看下 vue 官方文档
https://v2.cn.vuejs.org/v2/cookbook/avoiding-memory-leaks.html#%E7%AE%80%E4%BB%8B
场景业务场景非常简单,就是两个 tab 页面互相切换,但是业务终端不是 pc
而是我们工业互联网业务中的工控机
因为可执行内存只有 1g,所以会经常出现卡顿的情况
彦祖们可以想象成那种医院挂号的终端
场景复现操作流程彦祖们,看下我此时的界面(主要关注下 DOM节点)
查看站点组情况此时是 846 个
点击 单设备界面(单机界面)发现此时的 DOM节点 增加到了 1587 个
当然这是正常现象
多出来的节点个数不就是我这个 单机界面 渲染的节点个数吗?
回到 站点组界面惊人的发现 DOM 节点个数飙升到了 2862
不断来回切换在笔者来回切换了五六次之后,此时节点个数来到了 5477
排查问题猜想我们初步猜想是 单机界面 存在节点内存泄露,那么该如何来证明这个猜想呢?
DevTools 介绍我们需要借助 chrome devtools 来排查这个问题
打开 devtools 切到 内存 tab (英文版自行翻译)2 . 选取 时间轴上的分配插桩
3 . 点击回收垃圾(每次录制前需要手动回收,以保证数据准确)
在我们的 单机界面 点击开始录制
点击结束录制,我们能看到时间桩上会分布 蓝色/灰色 的柱状图
柱状图的分布展示了新内存分配时机
蓝色部分表示目前还占用的内存
灰色部分表示目前已被回收的内存
此案例中我们主要关心的是内存分布区域的构造函数
我们检测下有没有 Vue组件泄露
开始录制时间桩下面我们来模拟下业务中的场景
也就是站点组 单机界面来回切换,生成内存分布图
分析时间桩我们利用左右两边的框选工具,定位到第一次切换到单机界面的柱状图
此时我已经切到了 站点组,但是蓝色区域还有大部分没有被回收,让我们来看看是否有单机界面的内存泄露
过滤出 Vue
一个个检查下,是否有哪个组件只存在单机界面的但是没有被回收
这个 .alter-message 笔者全局搜了一下
并确保只有在 单机界面 使用
前文已经提到,业务中未使用 keep-alive 进行缓存
那么 vue 应该帮我们销毁这个组件了呀
查看组件详情我们来深入看下这个组件的信息,发现 vue 的确已经 destroy
这就很奇怪了,难道是所谓的 游离节点(https://juejin.cn/post/6984188410659340324#heading-5)?
不要慌,我们点击组件下面会展示内存分布详情
有些可能无法定位到代码文件,比如这种
这时候笔者就把 26 个泄露的文件一个个检查了一遍...(如果有更好的方式,欢迎评论区留言)
查看代码那么我们就进去看看代码,一探究竟,发现以下三处可疑的地方
一步步定位到这两段代码,彦祖们应该已经发现问题所在了
插入一段八股文此时不禁想起了这段八股文
面试官:造成内存泄露的情况有哪些?
彦祖: 未正确使用闭包 | 事件监听器未移除 | 定时器忘记清理 | 使用插件时,未销毁 | 意外的使用全局变量未回收 | console.log 未清除...
回到正题没错,我们这里就是命中了三个场景
事件监听器未移除定时器忘记清理使用 echarts 时,未销毁导致堆内存不知道你什么时候会销毁这个事件,这个定时器有没有用它也不知道啊~
这样一来也就长期劫持了对 vue组件 的引用
也就是说每次进入单机界面 都需要开辟新的内存以支持组件的渲染
看下这张图
如果一个界面中某个组件未被完全移除
那么整个界面的所有组件所占用的内存都不能被完全释放
验证猜想其实以上种种都是我们的猜想
接下来我们加上以下三行代码
移除全局事件监听window.removeEventListener('closeException',this.xxx)
移除全局定时器clearInterval(this.timeInterval)
销毁 echart 实例this.myChart.dispose()
再来抓一下内存分布
会发现已经没有泄露的 vue component 了
并且 DOM 节点 会被回收到 870 不会重复上升
改动3行代码,完美收工,走一个 998 ??
坑点彦祖们,补充一个坑点,在调试过程中,最好把 console 全去掉
笔者就在调试过程中遇到了一个坑,发现了这行代码
是的,打印了一下 vue 实例
console.log('实例',this)
结果导致内存怎么都回收不了
给问题定位带来了重大的干扰
不过这也完美解释了为什么 console.log 为什么会导致内存泄露
总结总结一下,如果彦祖们怀疑 A 页面有内存泄露,那么测试步骤如下
1.打开内存监视
2.A B A B 来回切换
3.关键!!!最后停留在 B 页面(不然 A 页面渲染了就会导致无法回收),停止内存监视
写在最后本次优化仅仅修改了3行代码
最复杂的问题,往往伴随着最简单的代码改动
但是问题的定位过程,是非常考验彦祖们的能力的
感谢彦祖们的阅读
个人能力有限
如有不对,欢迎指正??如有帮助,建议小心心大拇指三连??
点击关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线