vue单页应用的内存泄露定位和修复问题小结


Posted in Javascript onAugust 02, 2019

在前端项目(PC端)中,内存泄露的定位往往比修复更加困难,即使google浏览器有提供Memory工具,但是面对成千上万的元素和错综复杂的引用关系,开发则依然很难快速定位到问题代码块。

一、什么是内存泄漏?

系统进程不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。当内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。Chrome限制了浏览器所能使用的内存极限(64位为1.4GB,32位为1.0GB),这也就意味着浏览器将无法直接操作一些大内存对象。

V8引擎在执行垃圾回收时会阻塞 JavaScript应用逻辑,直到垃圾回收结束再重新执行JavaScript应用逻辑,这种行为被称为“全停顿”(stop-the-world)。 若V8的堆内存为1.5GB,V8做一次小的垃圾回收需要50ms以上,造成假死现象。

二、JS内存管理和垃圾回收机制GC

高级语言基本都有垃圾回收机制(garbage collection)自动管理内存,降低程序员的负担,以达到解决内存泄漏的目的,但是不允许人为手动触发,无法对内存管理进行任何干预。

老版本的浏览器使用引用计数法(Reference Counting)来管理内存,即每次引用加一,被释放时减一,当这个值的引用次数变成 0 时,就可以将其内存空间回收,缺点是循环引用时无法回收。

现代浏览器基本采用标记清除法(Mark-and-Sweep)来管理内存,即浏览器周期性地从某个根元素(譬如 window 对象)开始找引用变量,及这些变量引用的变量,这样一直找下去。能找到的变量为可获得变量,不能找到的将被内存回收。

vue单页应用的内存泄露定位和修复问题小结

缺点是清除后内存会产生很多细化的分块,所以又衍生了标记-整理法,不细讲。

vue单页应用的内存泄露定位和修复问题小结

三、VUE中容易出现内存泄露的几种情况

内存泄露是一个累积的过程,只有页面生命周期略长的时候才暴露出问题,频繁交互能够加快累积的过程,偏展示的页面很难把这样的问题暴露出来(所谓刷新一下又能满血复活)。所以很多时候我们都是被动式的等待问题暴露然后进行排查的,主动式的分析通常比较难。vue页面大多是单页应用,高交互且停留时间久,处理不好很容易出现内存泄漏。本文章 主要针对游离的dom对象进行排查 ,普通的JS变量排查有时间再补充。

1.全局变量造成的内存泄露

<template>
 <div id="home">
  这里是首页
 </div>
</template>

<script>
export default {
 mounted () {
  window.test = { // 此处在全局window对象中引用了本页面的dom对象
   name: 'home',
   node: document.getElementById('home')
  }
 }
}
</script>

按下Heap snapshots键,搜索Detached,发现没有脱离文档树的dom元素,属于正常现象

vue单页应用的内存泄露定位和修复问题小结

改变路由跳转到other页面,按下Heap snapshots键,搜索Detached,发现有两处dom元素游离于当前页面之外,很明显是window对象引用了home页面中的div,即使此时home页面已经销毁,home中的dom元素却还驻留在内存中无法释放。

vue单页应用的内存泄露定位和修复问题小结

解决方案就是在页面卸载的时候顺便处理掉该引用。

<template>
 <div id="home">
  这里是首页
 </div>
</template>

<script>
export default {
 mounted () {
  window.test = { // 此处在全局window对象中引用了本页面的dom对象
   name: 'home',
   node: document.getElementById('home')
  }
 },
 destroyed () {
  window.test = null // 页面卸载的时候解除引用
 }
}
</script>

2.除了直接引用,window的原生方法也会起到引用dom元素使其无法释放的效果。

<template>
 <div id="home">这里是首页</div>
</template>

<script>
export default {
 mounted () {
  window.addEventListener('resize', this.func) // window对象引用了home页面的方法
 },
 methods: {
  func () {
   console.log('这是home页面的函数')
  }
 }
}
</script>

vue单页应用的内存泄露定位和修复问题小结

 解决方法一样,也是在页面销毁的时候,顺便解除引用,释放内存

mounted () {
  window.addEventListener('resize', this.func)
},
beforeDestroy () {
  window.removeEventListener('resize', this.func)
}

3.一些全局的方法使用不当也会造成内存无法释放,在页面卸载的时候也可以考虑解除引用

<template>
 <div id="home">这里是首页</div>
</template>

<script>
export default {
 mounted () {
  this.$EventBus.$on('homeTask', res => this.func(res))
 },
 methods: {
  func (res) {
   console.log(res)
  }
 }
}
</script>

vue单页应用的内存泄露定位和修复问题小结

mounted () {
 this.$EventBus.$on('homeTask', res => this.func(res))
},
destroyed () {
 this.$EventBus.$off()
}

造成游离dom节点的原因还有很多,不止这三种,总结起来:

1.window对象、事件总线、全局vuex上绑定了已销毁页面上的节点,到时节点不随页面一起销毁

2.使用第三方库创建实例,第三方库一般会提供销毁函数,页面跳转时没有调用正确的销毁函数

3.有同学会说在页面中使用闭包也会造成内存泄露,在vue框架里有管理内存的机制,只要按照它的正确编写方法,理论上是不会造成内存泄露的

总结

以上所述是小编给大家介绍的vue单页应用的内存泄露定位和修复问题小结,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
javascript AOP 实现ajax回调函数使用比较方便
Nov 20 Javascript
原生Js实现按的数据源均分时间点幻灯片效果(已封装)
Dec 28 Javascript
JQuery伸缩导航练习示例
Nov 13 Javascript
使用C++为node.js写扩展模块
Apr 22 Javascript
深入理解JS addLoadEvent函数
May 20 Javascript
JavaScript中 this 指向问题深度解析
Feb 21 Javascript
简单谈谈vue的过渡动画(推荐)
Oct 11 Javascript
vue项目优化之通过keep-alive数据缓存的方法
Dec 11 Javascript
JavaScript实现邮箱后缀提示功能的示例代码
Dec 13 Javascript
js中call()和apply()改变指针问题的讲解
Jan 17 Javascript
微信小程序实现九宫格抽奖
Apr 15 Javascript
Node.js如何优雅的封装一个实用函数的npm包的方法
Apr 29 Javascript
vue回到顶部监听滚动事件详解
Aug 02 #Javascript
微信小程序npm引入vant-weapp的踩坑记录
Aug 01 #Javascript
vue3.0中的双向数据绑定方法及优缺点
Aug 01 #Javascript
微信小程序导入Vant报错VM292:1 thirdScriptError的解决方法
Aug 01 #Javascript
微信小程序使用Vant Weapp组件库的方法步骤
Aug 01 #Javascript
微信小程序中button去除默认的边框实例代码
Aug 01 #Javascript
vue-router两种模式区别及使用注意事项详解
Aug 01 #Javascript
You might like
php实现图片按比例截取的方法
2017/02/06 PHP
Bootstrap+PHP实现多图上传功能实例详解
2018/04/08 PHP
获取body标签的两种方法
2011/10/13 Javascript
jquery使用animate方法实现控制元素移动
2015/03/27 Javascript
JavaScript将DOM事件处理程序封装为event.js 出现的低级错误问题
2016/08/03 Javascript
jQuery Easyui Tabs扩展根据自定义属性打开页签
2016/08/15 Javascript
javascript经典特效分享 手风琴、轮播图、图片滑动
2016/09/14 Javascript
AngularJS ng-repeat指令中使用track by子语句解决重复数据遍历错误问题
2017/01/21 Javascript
Redux实现组合计数器的示例代码
2018/07/04 Javascript
玩转Koa之核心原理分析
2018/12/29 Javascript
详解一个基于套接字实现长连接的express
2019/03/28 Javascript
javascript实现超好看的3D烟花特效
2020/01/01 Javascript
[01:01:41]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Magma BO3 第二场 1月31日
2021/03/11 DOTA
在Python中使用zlib模块进行数据压缩的教程
2015/06/26 Python
Python tornado队列示例-一个并发web爬虫代码分享
2018/01/09 Python
致Python初学者 Anaconda入门使用指南完整版
2018/04/05 Python
django加载本地html的方法
2018/05/27 Python
在python里从协程返回一个值的示例
2019/02/19 Python
python实现图片上添加图片
2019/11/26 Python
python开发实例之Python的Twisted框架中Deferred对象的详细用法与实例
2020/03/19 Python
Python Pillow(PIL)库的用法详解
2020/09/19 Python
python3 通过 pybind11 使用Eigen加速代码的步骤详解
2020/12/07 Python
anaconda安装pytorch1.7.1和torchvision0.8.2的方法(亲测可用)
2021/02/01 Python
HTML5 Web 存储详解
2016/09/16 HTML / CSS
函授毕业自我鉴定
2014/02/04 职场文书
《难忘的泼水节》教学反思
2014/02/27 职场文书
大学四年个人自我小结
2014/03/05 职场文书
捐款倡议书格式范文
2014/05/14 职场文书
高中国旗下的演讲稿
2014/08/28 职场文书
大学生见习报告总结
2014/11/04 职场文书
工程部部长岗位职责
2015/02/12 职场文书
乱世佳人观后感
2015/06/08 职场文书
文艺节目主持词
2015/07/06 职场文书
医生行业员工的辞职信
2019/06/24 职场文书
MySQL创建管理LIST分区
2022/04/13 MySQL
Golang并发工具Singleflight
2022/05/06 Golang