JavaScript垃圾回收的机制很简单:
找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是时时的,
因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。
什么叫不再使用的变量?不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量,
全局变量的生命周期直至浏览器卸载页面才会结束。
局部变量只在函数的执行过程中存在,而在这个过程中会为局部变量在栈或堆上分配相应的空间,
以存储它们的值,然后再函数中使用这些变量,直至函数结束(闭包中由于内部函数的原因,外部函数并不能算是结束)
标记清除(mark and sweep)
这是JavaScript最常见的垃圾回收方式,
当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,
当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。
至于怎么标记有很多种方式,比如特殊位的反转、维护一个列表等,这些并不重要,重要的是使用什么策略,
大部分浏览器都是使用这种方式进行垃圾回收,区别在于如何标记及垃圾回收间隔而已,只有低版本IE,不出所料,又是IE。。。
引用计数(reference counting)
在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。
引用计数的策略是跟踪记录每个值被使用的次数,
当声明了一个变量B, 并将一个引用类型 A 赋值给该变量的时候, 这个值A的引用次数就加1,
如果该变量的值变成了另外一个,则这个值A的引用次数减1,
当这个值的引用次数变为0的时候,说明没有变量在使用,这个值没法被访问了,
因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
function test(){ var a = {} ; //a的引用次数为0 var b = a ; //a的引用次数加1,为1 var c =a; //a的引用次数再加1,为2 var b ={}; //a的引用次数减1,为1 }
看起来也不错的方式,为什么很少有浏览器采用,还会带来内存泄露问题呢?主要是因为这种方式没办法解决循环引用问题。
比如对象A有一个属性指向对象B,而对象B也有有一个属性指向对象A,这样相互引用
function test(){ var a={}; var b={}; a.prop=b; b.prop=a; }
这样a和b的引用次数都是2,即使在test()执行完成后,两个对象都已经离开环境,
在标记清除的策略下是没有问题的,离开环境的就被清除,但是在引用计数策略下不行,
因为这两个对象的引用次数仍然是2,不会变成0,所以其占用空间不会被清理,
如果这个函数被多次调用,这样就会不断地有空间不会被回收,造成内存泄露。
在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收,
但BOM与DOM对象却是通过引用计数回收垃圾的,也就是说只要涉及BOM及DOM就会出现循环引用问题。
看上面的例子,有同学回觉得太弱了,谁会做这样无聊的事情,其实我们是不是就在做
window.onload=function outerFunction(){ var obj = document.getElementById("element"); obj.onclick=function innerFunction(){}; };
这段代码看起来没什么问题,但是obj引用了document.getElementById("element"),
而document.getElementById("element")的onclick方法 innerFunction 会引用外部环境中的 活动变量 (arguments obj window等 ),
因为内部的函数会引用外部函数的整个作用域 来形成自己的作用域链
这个链自然也包括obj,是不是很隐蔽啊。
也就是obj的属性引用了obj本身
解决办法
最简单的方式就是自己手工解除循环引用,比如刚才的函数可以这样
window.onload=function outerFunction(){ var obj = document.getElementById("element"); obj.onclick=function innerFunction(){}; obj=null; };
什么时候触发垃圾回收
垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。
IE6的垃圾回收是根据内存分配量运行的,当环境中存在256个变量、4096个对象、64k的字符串任意一种情况的时候就会触发垃圾回收器工作,
看起来很科学,不用按一段时间就调用一次,有时候会没必要,这样按需调用不是很好吗?
但是如果环境中就是有这么多变量等一直存在,现在脚本如此复杂,很正常,那么结果就是垃圾回收器一直在工作,这样浏览器就没法儿玩儿了。
微软在IE7中做了调整,触发条件不再是固定的,而是动态修改的,初始值和IE6相同,
如果垃圾回收器回收的内存分配量低于程序占用内存的15%,说明大部分内存不可被回收,
设的垃圾回收触发条件过于敏感,这时候把临街条件翻倍,如果回收的内存高于85%,
说明大部分内存早就该清理了,这时候把触发条件置回。这样就使垃圾回收工作职能了很多。
同C# 、Java一样我们可以手工调用垃圾回收程序,但是由于其消耗大量资源,
而且我们手工调用的不会比浏览器判断的准确,所以不推荐手工调用垃圾回收。
http://www.jb51.net/article/75292.htm
http://www.cnblogs.com/dolphinX/p/3348468.html