JS中的垃圾回收机制

必要性:由于字符串、对象和数组没有固定大小,所有当他们的大小以知时,才能对他们进行动态的存储分配。JavaScript程度每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,javascript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。

这段话解释了为什么需要系统需要垃圾回收,JS不像C/C++,他有自己的一套垃圾回收机制。javascript的解析器可以检测到何时程序不再使用一个对象了,当他确定了一个对象是无用的时候,他就知道不再需要这个对象,可以把它所占用的内存释放掉了。

例如:

   

var a="hello world";
var b="world";
var a=b;

//这时,会释放掉“hello world”,释放内存以便再引用

垃圾回收的方法:标记清楚、计数引用。

标记清除

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

   可以使用任何方式来标记变量。比如,可以通过翻转某个特殊的位来记录一个变量何时进入环境,或者使用一个“进入环境的”变量列表及一个“离开环境的”变量列表来跟踪哪个变量发生变化。

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

 

引用计数(不常见)

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

用引用计数法会存在内存泄露,下面来看原因:

function problem() {
var objA = new Object();
var objB = new Object();
objA.someOtherObject = objB;
objB.anotherObject = objA;
}

 

     

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

特别是在DOM对象中,也容易存在这种问题:

var element=document.getElementById(’‘);
var myObj=new Object();
myObj.element=element;
element.someObject=myObj;

 

这样就不会有垃圾回收的过程。即使将DOM从页面中移除,它也永远不会被回收。,为了避免发生这样的问题,最好在他们不适用的时候手工断开原生js对象和dom元素之间的连接。

myobject.element = null;

element.someObject = null;

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

 

性能问题:

   触发垃圾收集的变量分配、字面量和(或)数组元素的临界值被调整为动态修正。如果垃圾收集例程回收的内存分配量低于15%,则变量、字面量和(或)数组元素的临界值就会加倍。如果例程回收了85%的内存分配量,则将各种临界值重置回默认值。 

posted on 2019-09-22 22:03  嗯嗯呢  阅读(3004)  评论(0编辑  收藏  举报