美文网首页
JavaScript的垃圾回收

JavaScript的垃圾回收

作者: HelloJames | 来源:发表于2018-01-06 20:47 被阅读13次

    JavaScript的垃圾回收

    前段时间读了一下<<JavaScript高级程序设计>>(书名很吓人, 实际上作者写得很好, 由浅入深), 了解了一下JavaScript的垃圾回收机制.

    和Java一样, JavaScript有自动垃圾回收机制.

    JavaScript的垃圾回收机制很简单:

    找出不再使用的变量, 然后释放掉其占用的内存, 但是这个过程不是实时的, 因为开销太大, 所以垃圾回收器会按照固定的时间周期性执行.

    变量生命周期

    变量主要分为: 全局变量和局部变量.
    全局变量的生命周期直至浏览器关闭页面才会结束, 而局部变量只在函数执行的过程中存在, 而在这个过程中会为局部变量在栈或堆上分配相应的内存空间, 以存储它的值, 然后再在函数中使用这些变量, 直至函数结束(闭包中由于内部函数的原因, 外部函数并不能算是结束了, 了解闭包可以看看JavaScript作用域链和闭包究竟是什么?)

    一旦函数结束, 局部变量就没有存在的必要了, 可以释放它们占用的内存.看似好像很简单的工作, 为什么会有很大的开销呢? 垃圾回收器需知道哪些变量有用, 哪些变量没用, 对于不再有用的变量打上标记, 以备将来回收. 用于标记无用的策略很多, 常见的有两种: 标记清除和引用计数.

    标记清除

    这是JavaScript常见的垃圾回收方式, 当变量进入执行环境时, 比如函数中声明一个变量, 垃圾回收器将其标记为"进入环境", 当变量离开环境的时候(函数执行结束), 将其标记为"离开环境". 至于怎么标记有很多方法. 比如特殊位的反转, 维护一个列表等.

    垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记, 然后去掉环境中的变量以及被环境变量所引用的变量(闭包), 在这些完成之后仍存在标记的就是要删除的变量, 因为环境中的变量已经无法访问到这些变量了, 然后垃圾回收器会清除这些标记的变量所占的内存空间.

    大部份的浏览都是使用这种方式进行垃圾回收, 区别在于如何标记及垃圾回收间隔罢了.只有低版本的IE...

    引用计数

    在低版本的IE中经常会出现内存泄露, 很多时候是因为其采用引用计数方式进行垃圾回收.

    引用计数的策略是:

    跟踪记录每个值被使用的次数, 当声明一个变量并将一个引用类型赋值给该变量的时候, 这个值的引用次数
    就加1, 如果该变量的值变成另一个, 则这个值的引用次数减1, 当这个值的引用次数为1时, 说明没有变量在使用, 这个值没法被访问了. 因此可以将其占用的内存空间回收. 这样垃圾回收器会在运行的时候清理掉引用次数为0的变量所占的内存空间.

    看起来不错的方式, 为什么很少有浏览器采用, 还会带来内存泄露呢?
    主要是因为这种方式没法解决循环引用问题. 比如对象A有一个属性指向对象B, 而对象B也有一个属性指向对象A, 这样的相互引用.

    在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收, 但BOM与DOM对象却是通过引用计数回收垃圾的,也就是说只要涉及BOM及DOM就很可能会出现循环引用的问题.如:

    window.onload = function outFunction(){
        var obj = document.getElementById("element");
        obj.onclick = function innerFunction(){};
    }
    

    这段代码看起来没什么问题, 但是obj引用了document.getElementById("element"), 而document.getElementById("element")的onclick方法会引用外部环境中的变量, 自然也包括obj, 是不是很隐蔽?

    解决办法:
    最简单的办法就是自己手工解除循环引用, 比如上面的例子:

    window.onload = function outFunction(){
        var obj = document.getElementById("element");
        obj.onclick = function innerFunction(){};
        obj = null;
    }
    

    什么时候触发垃圾回收

    垃圾回收器周期运行, 如果分配的内存非常多, 那么回收工作会很困难, 确定垃圾回收时间间隔变成了一个值得思考的问题. IE6的垃圾回收是根据内存分配量运行的, 当环境中存在256个变量, 4096个对象, 64K的字符串任意一种的情况下, 就会触发垃圾回收器工作, 看起来很科学, 不用按一段时间就调用一次, 但有时候会没有必要, 这样按需调用不是很好吗? 但如果环境中就是有这么多变量一直存在, 现在脚本如此复杂, 很正常, 那么结果就是垃圾回收器一直在工作, 这样浏览器就没法玩了.

    微软在IE7中做了调整, 触发条件不再是固定的, 而是动态修改的, 初始值和IE6相同, 如果垃圾回收器回收的内存分配量低于程序占用内存的15%, 说明大部分内存不可被回收, 设的垃圾回收触发条件过于敏感, 这时就把临界条件*2, 如果回收的内存高于85%, 说明大部分内存该清理了, 这时把触发条件重置, 这样就使得垃圾回收工作变得智能很多.

    同Java一样, 我们可以手工调用垃圾回收程序, 但由于其消耗大量资源, 而且手工调用的不会比浏览器判断的准确, 所以不推荐手工调用垃圾回收.

    相关文章

      网友评论

          本文标题:JavaScript的垃圾回收

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