设为首页 - 加入收藏 云计算计算网_厦门站长网 (http://www.0592zz.com)- 国内知名站长资讯网站,提供最新最全的站长资讯,创业经验,网站建设等!
热搜: 站长之家 2015 为什么 什么
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

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

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

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

?1. 介绍

浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大并且 GC 时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。

不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量,全局变量的生命周期直至浏览器卸载页面才会结束。局部变量只在函数的执行过程中存在,而在这个过程中会为局部变量在栈或堆上分配相应的空间,以存储它们的值,然后在函数中使用这些变量,直至函数结束,而闭包中由于内部函数的原因,外部函数并不能算是结束。

还是上代码说明吧:

  1. function?
  2. ?fn1()?{?
  3. ?var?obj?=?{name:?'hanzichi',?age:?10};?
  4. }?
  5. function?fn2()?{?
  6. ?var?obj?=?{name:'hanzichi',?age:?10};?
  7. ?return?obj;?
  8. }?
  9. var?a?=?fn1();?
  10. var?b?=?fn2();?

我们来看代码是如何执行的。首先声明了两个函数,分别叫做 fn1 和 fn2,当 fn1 被调用时,进入 fn1 的环境,会开辟一块内存存放对象 {name:'hanzichi',age:10},而当调用结束后,出了 fn1 的环境,那么该块内存会被 JS 引擎中的垃圾回收器自动释放;在 fn2 被调用的过程中,返回的对象被全局变量 b 所指向,所以该块内存并不会被释放。

这里问题就出现了:到底哪个变量是没有用的?所以垃圾收集器必须跟踪到底哪个变量没用,对于不再有用的变量打上标记,以备将来收回其内存。用于标记的无用变量的策略可能因实现而有所区别,通常情况下有两种实现方式:标记清除和引用计数。引用计数不太常用,标记清除较为常用。

2. 标记清除

js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。

  1. function?test(){?
  2. var?a?=?10?;?//?被标记?,进入环境?
  3. var?b?=?20?;?//?被标记?,进入环境?
  4. }?
  5. test();?//?执行完毕?之后?a、b又被标离开环境,被回收。?

垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。到目前为止,IE9+、Firefox、Opera、Chrome、Safari 的 JS 实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。

3. 引用计数

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为 0 的值所占用的内存。

  1. function?test()?{?
  2. ?var?a?=?{};?//?a指向对象的引用次数为1?
  3. ?var?b?=?a;?//?a指向对象的引用次数加1,为2?
  4. ?var?c?=?a;?//?a指向对象的引用次数再加1,为3?
  5. ?var?b?=?{};?//?a指向对象的引用次数减1,为2?
  6. }?

Netscape Navigator3 是最早使用引用计数策略的浏览器,但很快它就遇到一个严重的问题:循环引用。循环引用指的是对象 A 中包含一个指向对象B的指针,而对象 B 中也包含一个指向对象 A 的引用。

  1. function?fn()?{?
  2. ?var?a?=?{};?
  3. ?var?b?=?{};?
  4. ?a.pro?=?b;?
  5. ?b.pro?=?a;?
  6. }?
  7. fn();?

以上代码 a 和 b 的引用次数都是 2, fn 执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为 a 和 b 的引用次数不为 0,所以不会被垃圾回收器回收内存,如果 fn函数被大量调用,就会造成内存泄露。在 IE7 与 IE8 上,内存直线上升。

我们知道,IE 中有一部分对象并不是原生 JS 对象。例如,其内存泄露 DOM 和 BOM 中的对象就是使用 C++ 以 COM 对象的形式实现的,而 COM 对象的垃圾回收机制采用的就是引用计数策略。因此,即使IE的js引擎采用标记清除策略来实现,但 JS 访问的COM对象依然是基于引用计数策略的。换句话说,只要在 IE 中涉及 COM 对象,就会存在循环引用的问题。

  1. var?element?=?document.getElementById("some_element");?
  2. var?myObject?=?new?Object();?
  3. myObject.e?=?element;?
  4. element.o?=?myObject;?

【免责声明】本站内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

网友评论
推荐文章