美文网首页
JavaScript垃圾回收机制

JavaScript垃圾回收机制

作者: 前端开发_Eric | 来源:发表于2019-06-22 17:29 被阅读0次

    在介绍垃圾回收机制之前先了解一个概念:内存泄漏。

    什么是内存泄漏?

    程序的运行需要内存,只要程序提出要求,操作系统在运行时就必须供给内存,对于持续运行的服务进程,必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。而不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。

    对于任何一门语言,在运行过程中都会创建许多对象,继而需要为这些对象分配内存地址,但是内存并不是无限的,对于那些我们不再需要的变量、对象该怎么处理呢?需要释放其占用的内存地址,以供新的对象使用。关于对象内存释放的这一机制就叫做垃圾回收机制(Garbage collection)。
    难道内存要一个一个去手动释放么?其实并不需要,Javascript具有自动垃圾回收机制,垃圾收集器会按照固定的时间间隔周期性的执行,对那些我们不再使用的变量、对象所占用的内存进行释放。

    JS垃圾回收方法:

    JS执行环境中的垃圾回收器怎样才能检测哪块内存可以被回收有两种方式:标记清除(mark and sweep)、引用计数(reference counting)。

    1.标记清除方式(mark and sweep)

    JavaScript 中最常用的垃圾收集方式是标记清除。
    工作原理:当变量进入环境(函数中声明变量)时,将这个变量标记为“进入环境”。当变量离开环境(函数执行结束)时,则将其标记为“离开环境”。标记“离开环境”的就回收其所对应的内存。
    工作流程:
    1. 垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
    2. 去掉环境中的变量以及被环境中的变量引用的变量的标记。
    3. 再被加上标记的会被视为准备删除的变量。
    4. 垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。

    2.引用计数方式 (reference counting)

    这种方式常常会引起内存泄漏,低版本的IE使用这种方式。
    工作原理:跟踪记录每个值被引用的次数。
    工作流程:
    1. 声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1。

    var  arr = [1, 2, 3, 4];
    

    例如,上面代码中,数组[1, 2, 3, 4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它还是会持续占用内存。

    1. 同一个值又被赋值给另一个变量,这个引用类型值的引用次数加1。
    var  arr = [1, 2, 3, 4];
    var newArr = arr;
    

    此时该引用类型值的引用次数为2。

    1. 当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减1。
    var  arr = [1, 2, 3, 4];
    arr = [1,2,3,4,5,6];
    

    这段代码运行之后,[1, 2, 3, 4]这个数组失去了引用(之前是被arr引用),
    系统检测到这个事实之后,就会释放该数组的存储空间以便这些空间可以被再次利用。

    1. 当引用次数变成0时,说明没办法访问这个值了。
    var arr = [1, 2, 3, 4];
    arr = null;
    

    这行代码中将arr重置为null,就解除了对[1, 2, 3, 4]的引用,引用次数变成了0。

    1. 当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存。
      该方式会引起内存泄漏,原因是它不能解决循环引用的问题,循环引用就是对象A中包含指向对象B的指针,对象B中也包含一个指向A的指针。
    function fn() {
        var objA = new Object();
        var objB = new Object();
        objA.prop = objB;
        objB.prop = objA;
    }
    fn();
    

    在这个例子中,objA和objB通过各自的属性相互引用,这两个对象的引用次数都是2。在采用引用计数的策略中,函数执行之后,这两个对象都离开了作用域,但objA和objB还将会继续存在,因为他们的引用次数永远不会是0,会使这部分内存永远不会被释放,即内存泄露。

    低版本IE中有一部分对象并不是原生JS对象。例如,其BOM和DOM中的对象就是使用C++以COM(Component Object Model)对象的形式实现的,而COM对象的垃圾收集机制采用的就是引用计数策略。

    因此即使IE的js引擎是用的标记清除来实现的,但是js访问COM对象如BOM,DOM还是基于引用计数的策略的,也就是说只要在IE中设计到COM对象,也就会存在循环引用的问题。

    例如下方代码,当一个DOM元素和一个原生的js对象之间的循环引用时,obj和ele对象的引用次数都为2,这部分内存不会被释放。

    var ele = document.getElementById("eleId");
    var obj = {};
    obj.property = ele;
    ele.property = obj;
    

    将obj和ele的property重置为null, 即可解除原生JS对象与DOM元素之间的连接。

    obj.property = null;
    ele.property = null;
    

    IE9把DOM和BOM转换成真正的JS对象了,所以避免了这个问题。
    今天就先分享这么多,文中如有错误或不当之处,还望同仁指正。

    相关文章

      网友评论

          本文标题:JavaScript垃圾回收机制

          本文链接:https://www.haomeiwen.com/subject/omluqctx.html