JavaScript 垃圾回收

在公司常常会听到大牛们讨论时说道内存泄露神马的,往往都惊羡不已,最近精力主要用在了Web 开发上,读了一下《JavaScript高级程序设计》(书名很唬人,实际做者写的特别好,由浅入深)了解了一下JavaScript垃圾回收机制,对内存泄露有了必定的认识。html

和C#、Java同样JavaScript有自动垃圾回收机制,也就是说执行环境会负责管理代码执行过程当中使用的内存,在开发过程当中就无需考虑内存分配及无用内存的回收问题了。JavaScript垃圾回收的机制很简单:找出再也不使用的变量,而后释放掉其占用的内存,可是这个过程不是时时的,由于其开销比较大,因此垃圾回收器会按照固定的时间间隔周期性的执行。浏览器

变量生命周期

有同窗看了上面就会问了,什么叫再也不使用的变量?再也不使用的变量也就是生命周期结束的变量,固然只多是局部变量,全局变量的生命周期直至浏览器卸载页面才会结束。局部变量只在函数的执行过程当中存在,而在这个过程当中会为局部变量在栈或堆上分配相应的空间,以存储它们的值,而后再函数中使用这些变量,直至函数结束(闭包中因为内部函数的缘由,外部函数并不能算是结束,了解闭包能够看看 JavaScript做用域链JavaScript 闭包到底是什么)。闭包

一旦函数结束,局部变量就没有存在必要了,能够释放它们占用的内存。猫和很简单的工做,为何会有很大开销呢?这仅仅是垃圾回收的冰山一角,就像刚刚提到的闭包,貌似函数结束了,其实尚未,垃圾回收器必须那个变量游泳,那个变量没用,对于再也不有用的变量打上标记,以备未来回收。用于标记无用的策略有不少,常见的有两种方式函数

标记清除(mark and sweep)

这是JavaScript最多见的垃圾回收方式,当变量进入执行环境的时候,好比函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。至于怎么标记有不少种方式,好比特殊位的反转、维护一个列表等,这些并不重要,重要的是使用什么策略,原则上讲不可以释放进入环境的变量所占的内存,它们随时可能会被调用的到。spa

垃圾回收器会在运行的时候给存储在内存中的全部变量加上标记,而后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成以后仍存在标记的就是要删除的变量了,由于环境中的变量已经没法访问到这些变量了,而后垃圾回收器相会这些带有标记的变量机器所占空间。设计

大部分浏览器都是使用这种方式进行垃圾回收,区别在于如何标记及垃圾回收间隔而已,只有低版本IE,不出所料,又是IE。。。code

引用计数(reference counting)

在低版本IE中常常会出现内存泄露,不少时候就是由于其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每一个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,若是该变量的值变成了另一个,则这个值得引用次数减1,当这个值的引用次数变为0的时候,说明没有变量在使用,这个值无法被访问了,所以能够将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。htm

看起来也不错的方式,为何不多有浏览器采用,还会带来内存泄露问题呢?主要是由于这种方式没办法解决循环引用问题。好比对象A有一个属性指向对象B,而对象B也有有一个属性指向对象A,这样相互引用对象

function test(){
            var a={};
            var b={};
            a.prop=b;
            b.prop=a;
        }

这样a和b的引用次数都是2,即便在test()执行完成后,两个对象都已经离开环境,在标记清除的策略下是没有问题的,离开环境的就被清除,可是在引用计数策略下不行,由于这两个对象的引用次数仍然是2,不会变成0,因此其占用空间不会被清理,若是这个函数被屡次调用,这样就会不断地有空间不会被回收,形成内存泄露。blog

在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方法会引用外部环境中德变量,天然也包括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同样咱们能够手工调用垃圾回收程序,可是因为其消耗大量资源,并且咱们手工调用的不会比浏览器判断的准确,因此不推荐手工调用垃圾回收。

相关文章
相关标签/搜索