导航

关于javascript中的垃圾回收机制

Posted on 2014-08-17 22:23  veitch07  阅读(199)  评论(0)    收藏  举报

  好长时间没写博客了,前面陆陆续续学习了js,AJAX,node.js,html5,seo等技术的基础,然后这两天又接触了jQuery,less,sass几个框架。总的来说,学习到的技术很多,但是都只是触及到一些皮毛,对于这些技术一些高级的应用以及内部的一些机制其实还不是很明白。哎~前端水深,同志还需要继续努力啊!

  今天天朗气清,惠风和畅,写个博客聊一聊JS的垃圾回收机制吧。

 JavaScript具有自动垃圾收集机制。也就是说 执行环境会负责管理代码执行过程中使用的内存,这样,JS的开发人员就不用再像C、C++之类的语言那样关心内存的使用问题。JSd的垃圾收集器会按照固定的时间间隔,周期性地找出那些不再继续使用的变量,并释放其占用的内存空间。

 要讲清楚回收机制就得先说明一下函数中局部变量的整常生命周期。在函数执行的过程中,会为每个局部变量在内存上分配相应的空间。当函数执行结束,局部变量就没有存在的必要了,因此可以释放他们的内存以便将来使用。为此,垃圾收集器必须跟踪哪个变量有用,基本上,实现这一目标大致有两个策略。

 一.标记清除
  JS中最常用的垃圾收集方式就是标记-清除(mark-and-sweep)。当变量进入环境时,就将这个变量标记为“进入环境”。而当变量离开环境时,则将其标记为“离开环境”。

   垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。

  到2008年为止,IE、Firefox、Opera、Chrome和Safari的JavaScript实现使用的都是标记清除式的垃圾收集策略(或类似的策略),只不过垃圾收集的时间间隔互补相同。

二、引用计数

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

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

  function problem(){

    var objectA=new Object();

    var objectB=new Object();

    objectA.someOtherObject=objectB;

    objectB.anotherObject=ObjectA;

}

  在这个例子中,objectA和ObjectB通过各自的属性相互引用;也就是说,这两个对象的引用次数都是2。在采用标记清除策略的实现中,由于函数执行之后,这两个对象都离开了作用域,因此这种相互引用不是个问题。但在采用引用计数策略的实现中,当函数执行完毕后,objectA和objectB还将继续存在,因为他们的引用次数永远不会是0.假如这个函数被重复多次调用,就会导致大量的内存得不到回收。为此Netscape在Navigator4.0中放弃了引用计数方式,转而采用标记清除的方式来实现其垃圾回收。

  引用计数带来的麻烦最经典的例子就是给IE带来的内存泄露问题。我们知道,IE中有一部分对象并不是原生的Javascript对象。例如,其BOM和DOM中的对象就是使用C++以以COM(组件对象模型)对象的形式实现的,而COM对象的垃圾回收机制就是引用计数。因此,即使IE的Javascript引擎是使用标记清除策略来实现的,但javascript访问的COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题。考虑下面这个例子:

  var element=document.getElementById("some_element");

  var obj=new object();

  obj.element=element;

  element.someobj=obj;

  这个例子中存在一个DOM元素(element)与一个原生javascript对象(obj)之间的循环引用。在这种情况下,即使DOM元素被移除,它也永远不会被回收。

  为了避免这样的循环引用问题,最好在不使用它们的时候手动断开它们之间的连接:

  obj.element=null;

  element.obj=null;

  这样,当垃圾收集器下次运行时,就会删除这些值并回收他们占用的内存。

  为了解决上述问题,IE9把BOM和DOM都转换成了真正的javascript对象,这样,上述问题就得以避免。

  好了,时间不早了,let`s call it a day!