JavaScript 垃圾回收机制分析


Posted in Javascript onOctober 10, 2013

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

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

变量生命周期

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

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

标记清除(mark and sweep)

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

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

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

引用计数(reference counting)

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

看起来也不错的方式,为什么很少有浏览器采用,还会带来内存泄露问题呢?主要是因为这种方式没办法解决循环引用问题。比如对象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方法会引用外部环境中德变量,自然也包括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一样我们可以手工调用垃圾回收程序,但是由于其消耗大量资源,而且我们手工调用的不会比浏览器判断的准确,所以不推荐手工调用垃圾回收。

Javascript 相关文章推荐
Firefox window.close()的使用注意事项
Apr 11 Javascript
Google Map API更新实现用户自定义标注坐标
Jul 29 Javascript
js中点击空白区域时文本框与隐藏层的显示与影藏问题
Aug 26 Javascript
js螺旋动画效果的具体实例
Nov 15 Javascript
js控制输入框获得和失去焦点时状态显示的方法
Jan 30 Javascript
js实现精美的图片跟随鼠标效果实例
May 16 Javascript
jquery移除了live()、die(),新版事件绑定on()、off()的方法
Oct 26 Javascript
jQuery实现在HTML文档加载完毕后自动执行某个事件的方法
May 08 jQuery
jQuery插件select2利用ajax高效查询大数据列表(可搜索、可分页)
May 19 jQuery
JS 判断某变量是否为某数组中的一个值的3种方法(总结)
Jul 10 Javascript
微信小程序实现提交input信息到后台的方法示例
Jan 19 Javascript
JavaScript设计模式---单例模式详解【四种基本形式】
May 16 Javascript
jQuery的attr与prop使用介绍
Oct 10 #Javascript
JavaScript 数组详解
Oct 10 #Javascript
js导出table数据到excel即导出为EXCEL文档的方法
Oct 10 #Javascript
Jquery attr("checked") 返回checked或undefined 获取选中失效
Oct 10 #Javascript
Javascript Web Slider 焦点图示例源码
Oct 10 #Javascript
JS实现程序暂停与继续功能代码解读
Oct 10 #Javascript
js切换光标示例代码
Oct 10 #Javascript
You might like
PHP5.5和之前的版本empty函数的不同之处
2014/06/13 PHP
php 使用array函数实现分页
2015/02/13 PHP
javascript document.execCommand() 常用解析
2009/12/14 Javascript
JS 如果改变span标签的是否隐藏属性
2011/10/06 Javascript
jQuery+PHP+Mysql实现抽奖程序
2020/04/12 jQuery
JavaScript利用fetch实现异步请求的方法实例
2017/07/26 Javascript
angular1配合gulp和bower的使用教程
2018/01/19 Javascript
vuex与组件联合使用的方法
2018/05/10 Javascript
vue.js过滤器+ajax实现事件监听及后台php数据交互实例
2018/05/22 Javascript
解决vue2.0路由跳转未匹配相应用路由避免出现空白页面的问题
2018/08/24 Javascript
Vue实现调节窗口大小时触发事件动态调节更新组件尺寸的方法
2018/09/15 Javascript
如何在基于vue-cli的项目自定义打包环境
2018/11/10 Javascript
详解Vue源码之数据的代理访问
2018/12/11 Javascript
vue实现微信获取用户信息的方法
2019/03/21 Javascript
vue+elementUI实现表格关键字筛选高亮
2020/10/26 Javascript
Vue.js+cube-ui(Scroll组件)实现类似头条效果的横向滚动导航条
2019/06/24 Javascript
Python中列表、字典、元组、集合数据结构整理
2014/11/20 Python
实例解析Python中的__new__特殊方法
2016/06/02 Python
解决Python requests 报错方法集锦
2017/03/19 Python
python两个list[]相加的实现方法
2020/09/23 Python
详解CSS3的perspective属性设置3D变换距离的方法
2016/05/23 HTML / CSS
HTML实现代码雨源码及效果示例
2020/02/25 HTML / CSS
域名注册、建站工具、网页主机、SSL证书:Dynadot
2017/01/06 全球购物
be2台湾单身男女交友:全球网路婚姻介绍的领导品牌
2019/10/11 全球购物
易程科技软件测试笔试
2013/03/24 面试题
大客户销售经理职责
2013/12/04 职场文书
建筑班组长岗位职责
2014/01/02 职场文书
服务型党组织建设典型材料
2014/05/07 职场文书
银行服务明星推荐材料
2014/05/29 职场文书
应届大专生求职信
2014/06/26 职场文书
委托书格式范文
2015/01/28 职场文书
检讨书格式
2015/05/07 职场文书
化工生产实习心得体会
2016/01/22 职场文书
新店开业策划方案怎么书写?
2019/07/05 职场文书
导游词之日月潭
2019/11/05 职场文书
Spring-cloud Config Server的3种配置方式
2021/09/25 Java/Android