当前位置: 首页 > 科技观察

闭包会导致内存泄漏吗?

时间:2023-03-19 17:01:31 科技观察

前言在说内存泄漏之前,我们先来看看JavaScript的垃圾回收机制。JavaScript有一个自动垃圾回收机制,就是把那些不再使用的变量找出来,然后释放它们占用的内存。为此,垃圾收集器遵循固定的时间间隔(或代码执行期间的预定收集时间)。常用的方法有两种,即清除标记和引用计数。1.Mark-and-sweepJavaScript中最常见的垃圾回收方法是mark-and-sweep。垃圾收集器会在运行时标记所有存储在内存中的变量(你可以使用任何标记方法)。然后它剥离环境中的变量和环境中变量引用的变量。之后被标记的变量将被视为要删除的变量,因为环境中的变量无法再访问这些变量。最后,垃圾回收器完成内存清理工作,销毁那些标记的值,回收它们占用的内存空间。2.引用计数引用计数(referencecounting)的含义是跟踪记录每个值被引用的次数。当声明一个变量,并为该变量赋一个引用类型值时,该值的引用计数为1。如果将相同的值赋给另一个变量,则该值的引用计数加1。反之,如果包含对该值的引用的变量取了另一个值,该值的引用计数减1。当对该值的引用数变为0时,意味着无法访问该值,因此它占用的内存空间可以被恢复。这样,下次垃圾收集器运行时,它将释放那些零引用值占用的内存。NetscapeNavigator3.0是第一个使用引用计数策略的浏览器,但很快就遇到了一个严重的问题,看下面的例子:.anotherObject=objectA;}解释:objectA和objectB通过各自的属性互相引用,即两个对象的引用次数都是2。两个对象都超出作用域,所以这个交叉引用没有问题.但是在使用引用计数策略的实现中,当函数执行时,objectA和objectB也表明它们会继续存在,因为它们的引用计数永远不会为0。如果重复调用这个函数,会占用大量内存无法恢复。为此,Netscape在Navigator4.0中放弃了引用计数方式,但引用计数带来的麻烦并没有就此结束。在IE9之前,有些对象不是原生的JavaScript对象。例如,其BOM和DOM中的对象使用C++以COM(ComponentObjectModel,组件对象模型)对象的形式实现,COM对象的垃圾回收机制采用引用计数策略。因此,尽管IE的JavaScript引擎是使用标记清除策略实现的,但JavaScript访问的COM对象仍然是基于引用计数策略。也就是说,只要在IE中涉及到COM对象,就会出现循环引用问题。例如:varelement=document.getElementById("some_element");varmyObject=newObject();myObject.element=element;element.someObject=myObject;在DOM元素(element)和原生JavaScript对象(myObject)之间创建循环引用。其中,变量myObject有一个名为element的属性,指向元素对象;并且变量元素还有一个名为someObject的属性,它引用回myObject。因为这个循环引用,即使示例中的DOM从页面中移除,它也永远不会被回收。解决方案:将变量设置为null以切断变量与其先前引用的值之间的连接。myObject.element=null;element.someObject=null;看完以上内容,我来说说正题。闭包不会导致内存泄漏,因为IE9之前的版本对JScript对象和COM对象使用不同的垃圾回收。所以闭包会在这些版本的IE中引起一些特殊的问题。具体来说,如果一个HTML元素存储在闭包的作用域链中,则意味着该元素不能被销毁。请参见示例:functionassignHandler(){varelement=document.getElementById("someElement");element.onclick=function(){alert(element.id);};}上面的代码创建了一个闭包作为元素的事件处理程序元素,这又会创建一个循环引用。由于匿名函数持有对assignHandler()活动对象的引用,因此不可能减少对元素的引用数。只要匿名函数存在,element的引用数至少为1,所以它占用的内存永远不会被回收。解决方案如前言所述,将element.id的副本保存在一个变量中,以消除闭包中元素变量中对该变量的循环引用也设置为null。functionassignHandler(){varelement=document.getElementById("someElement");varid=element.id;element.onclick=function(){alert(id);};element=null;}总结:闭包不会造成内存泄漏只是因为IE9之前的版本对JScript对象和COM对象使用了不同的垃圾回收方式,导致内存无法回收。这是IE的问题,所以闭包与内存泄漏无关。本文已经做了详细的测试,有兴趣的可以点击查看