加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 建站 > 正文

浏览器垃圾回收机制与 Vue 项目内存泄漏场景分析

发布时间:2019-09-12 16:05:49 所属栏目:建站 来源:全栈高手
导读:1. 介绍 浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其

这个例子在一个 DOM 元素 element 与一个原生js对象 myObject 之间创建了循环引用。其中,变量 myObject 有一个属性 e 指向 element 对象;而变量 element 也有一个属性 o 回指 myObject。由于存在这个循环引用,即使例子中的 DOM 从页面中移除,它也永远不会被回收。

举个栗子:

浏览器垃圾回收机制与 Vue 项目内存泄漏场景分析
  • 黄色是指直接被 js变量所引用,在内存里
  • 红色是指间接被 js变量所引用,如上图,refB 被 refA 间接引用,导致即使 refB 变量被清空,也是不会被回收的
  • 子元素 refB 由于 parentNode 的间接引用,只要它不被删除,它所有的父元素(图中红色部分)都不会被删除

另一个例子:

  1. window.onload=function outerFunction(){ 
  2.  var obj = document.getElementById("element"); 
  3.  obj.onclick=function innerFunction(){}; 
  4. }; 

这段代码看起来没什么问题,但是 obj 引用了 document.getElementById('element'),而 document.getElementById('element') 的 onclick 方法会引用外部环境中的变量,自然也包括 obj,是不是很隐蔽啊。(在比较新的浏览器中在移除Node的时候已经会移除其上的event了,但是在老的浏览器,特别是 IE 上会有这个 bug)

解决办法:

最简单的方式就是自己手工解除循环引用,比如刚才的函数可以这样

  1. myObject.element = null; 
  2. element.o = null; 
  3.  
  4. window.onload=function outerFunction(){ 
  5.  var obj = document.getElementById("element"); 
  6.  obj.onclick=function innerFunction(){}; 
  7.  obj=null; 
  8. }; 

将变量设置为 null 意味着切断变量与它此前引用的值之间的连接。当垃圾回收器下次运行时,就会删除这些值并回收它们占用的内存。

要注意的是,IE9+ 并不存在循环引用导致 DOM 内存泄露问题,可能是微软做了优化,或者 DOM 的回收方式已经改变。

4. 内存管理

4.1 什么时候触发垃圾回收?

垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。IE6的垃圾回收是根据内存分配量运行的,当环境中存在256个变量、4096个对象、64k的字符串任意一种情况的时候就会触发垃圾回收器工作,看起来很科学,不用按一段时间就调用一次,有时候会没必要,这样按需调用不是很好吗?但是如果环境中就是有这么多变量等一直存在,现在脚本如此复杂,很正常,那么结果就是垃圾回收器一直在工作,这样浏览器就没法儿玩儿了。

微软在 IE7 中做了调整,触发条件不再是固定的,而是动态修改的,初始值和 IE6 相同,如果垃圾回收器回收的内存分配量低于程序占用内存的15%,说明大部分内存不可被回收,设的垃圾回收触发条件过于敏感,这时候把临街条件翻倍,如果回收的内存高于 85%,说明大部分内存早就该清理了,这时候把触发条件置回。这样就使垃圾回收工作职能了很多

4.2 合理的GC方案

1. 基础方案

Javascript 引擎基础GC方案是(simple GC):mark and sweep(标记清除),即:

遍历所有可访问的对象。

回收已不可访问的对象。

2. GC的缺陷

和其他语言一样,JS 的 GC 策略也无法避免一个问题:GC 时,停止响应其他操作,这是为了安全考虑。而 Javascript 的 GC 在 100ms 甚至以上,对一般的应用还好,但对于 JS 游戏,动画对连贯性要求比较高的应用,就麻烦了。这就是新引擎需要优化的点:避免GC造成的长时间停止响应。

3. GC优化策略

David 大叔主要介绍了2个优化方案,而这也是最主要的2个优化方案了:

分代回收(Generation GC)

这个和Java回收策略思想是一致的,也是V8所主要采用的。目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时。如图:

浏览器垃圾回收机制与 Vue 项目内存泄漏场景分析

这里需要补充的是:对于 tenured generation 对象,有额外的开销:把它从 young generation 迁移到 tenured generation,另外,如果被引用了,那引用的指向也需要修改。这里主要内容可以参考深入浅出Node中关于内存的介绍,很详细~

2. 增量GC 这个方案的思想很简单,就是“每次处理一点,下次再处理一点,如此类推”。如图:

浏览器垃圾回收机制与 Vue 项目内存泄漏场景分析

这种方案,虽然耗时短,但中断较多,带来了上下文切换频繁的问题。因为每种方案都其适用场景和缺点,因此在实际应用中,会根据实际情况选择方案。比如:低 (对象/s) 比率时,中断执行GC的频率,simple GC更低些;如果大量对象都是长期“存活”,则分代处理优势也不大。

4. Vue 中的内存泄漏问题

JS 程序的内存溢出后,会使某一段函数体永远失效(取决于当时的 JS 代码运行到哪一个函数),通常表现为程序突然卡死或程序出现异常。

这时我们就要对该 JS 程序进行内存泄漏的排查,找出哪些对象所占用的内存没有释放。这些对象通常都是开发者以为释放掉了,但事实上仍被某个闭包引用着,或者放在某个数组里面。

4.1 泄漏点

DOM/BOM 对象泄漏;

script 中存在对 DOM/BOM 对象的引用导致;

JS 对象泄漏;

通常由闭包导致,比如事件处理回调,导致 DOM 对象和脚本中对象双向引用,这个是常见的泄漏原因;

4.2 代码关注点

主要关注的就是各种事件绑定场景,比如:

DOM 中的 addEventLisner 函数及派生的事件监听,比如 Jquery 中的 on 函数,Vue 组件实例的 $on 函数;

其它 BOM 对象的事件监听, 比如 websocket 实例的 on 函数;

避免不必要的函数引用;

如果使用 render 函数,避免在 HTML 标签中绑定 DOM/BOM 事件;

4.3 如何处理

如果在 mounted/created 钩子中使用 JS 绑定了 DOM/BOM 对象中的事件,需要在 beforeDestroy 中做对应解绑处理;

如果在 mounted/created 钩子中使用了第三方库初始化,需要在 beforeDestroy 中做对应销毁处理(一般用不到,因为很多时候都是直接全局 Vue.use);

如果组件中使用了 setInterval,需要在 beforeDestroy 中做对应销毁处理;

4.4 在 vue 组件中处理 addEventListener

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读