`
oznyang
  • 浏览: 160949 次
  • 性别: Icon_minigender_1
  • 来自: 南京
文章分类
社区版块
存档分类
最新评论

IE下闭包引起跨页面内存泄露探讨

阅读更多
在ie的内存泄露中跨页面的泄露是最严重的,浏览器刷新了仍然无法释放掉泄露占用的资源,造成访问速度越来越慢,内存占用越来越大
closure引起cross page leak的主要原因是closure和dom元素的互相引用
看这个例子:
<div id="bb"><div id="aa">cc</div></div>
<script type="text/javascript">
function leakTest(){
	var a=[];//用来加大闭包资源占用,方便观察
	for(var i=0;i<100000;i++){
	 a.push('a');
	}
	var divA=document.getElementById('aa');
	divA.kk=function(){};
	divA.parentNode.removeChild(divA);
}
leakTest();
</script>

    用sIEve看下发现这个页面每次刷新都会产生跨页面泄露,ie内存占用大了7MB,具体fins的文章中有过介绍
在这个例子中我们在leakTest()中创建了一个内部匿名函数并在dom元素aa的自定义属性kk中保存了他的引用,这就产生了一个闭包
divA.parentNode.removeChild(divA);
这句是产生泄露的主要原因,移除了aa并不会使这个节点消失,只不过在dom树上无法访问这个节点而已,由于闭包的存在divA这个局部变量不会被释放,而divA中保存着aa的引用,这就形成了一个循环引用,闭包保存了dom元素aa的引用,dom元素aa的自定义属性kk又保存了闭包内部的匿名函数的引用,所以在页面刷新的时候IE无法释放掉这个aa和闭包的资源,在我这个例子中就比较吓人,刷一下涨几MB内存
    让我们删掉divA.parentNode.removeChild(divA);这句试试,发现没有泄露发生
    我推测IE在刷新时会强行释放掉dom树上的元素,而不存在于dom树中的节点不会强行释放,所以造成了跨页面泄露,这是我的个人推测,有别的意见欢迎讨论
怎么解决这个问题呢,其实我们只要打断引用链就行了
<div id="bb"><div id="aa">cc</div></div>
<script type="text/javascript">
function leakTest(){
	var a=[];
	for(var i=0;i<100000;i++){
	 a.push('a');
	}
	var divA=document.getElementById('aa');
	divA.kk=function(){};
	divA.parentNode.removeChild(divA);
	divA=null;
}
leakTest();
</script>

或者
<div id="bb"><div id="aa">cc</div></div>
<script type="text/javascript">
function leakTest(){
	var a=[];
	for(var i=0;i<100000;i++){
	 a.push('a');
	}
	document.getElementById('aa').kk=function(){};
	document.getElementById('aa').parentNode.removeChild(document.getElementById('aa'));
	//这个例子不保存aa的应用,也不会引起泄露
}
leakTest();
</script>

or
<div id="bb"><div id="aa">cc</div></div>
<script type="text/javascript">
function leakTest(){
	var a=[];
	for(var i=0;i<100000;i++){
	 a.push('a');
	}
	var divA=document.getElementById('aa');
	divA.kk=function(){};
	divA.parentNode.removeChild(divA);
	return divA;
}
var divA=leakTest();
divA.kk=null; //这个可以看到内存占用比上面少了7MB,因为解除了对闭包内部函数的引用,闭包占用的资源被释放了
</script>

通过上面的例子可以看出,如果某个函数中dom元素保存了内部函数的引用,就会形成闭包,很容易引起泄露,务必小心
另firefox下测试是没有这些问题的

分享到:
评论
37 楼 pipilu 2009-02-06  
trarck 写道
请注意:
  这是楼主的代码,也是会产生内存泄露的代码:
  
<div id="bb"><div id="aa">cc</div></div>  
    <script type="text/javascript">  
    function leakTest(){  
        var a=[];//用来加大闭包资源占用,方便观察  
       for(var i=0;i<100000;i++){  
         a.push('a');  
        }  
       var divA=document.getElementById('aa');  
        divA.kk=function(){};  
       divA.parentNode.removeChild(divA);  
   }  
   leakTest();  
    </script>  

   这是liudaoru的代码,但没有产生内存泄露(不信你可以试试)
  
 <div id="bb"><div id="aa">cc</div></div>     
 <script type="text/javascript">     
 function leakTest(){     
     var a=[];//用来加大闭包资源占用,方便观察     
     for(var i=0;i<100000;i++){     
      a.push('a');     
     }     
     this.divA=document.getElementById('aa');     
     this.divA.kk=function(){};     
     document.getElementById("bb").removeChild(this.divA);     
 }     
 new leakTest();  //产生个匿名对象   
 </script> 


为什么呢?如此相似的代码,一个产生了泄露一个却没有。
个人观点:
楼主用一个var定义了个局部变量divA,闭包<=>div就有了循环引用。
liudaoru用this。this.divA没有被闭包占用(可能是this的特殊性)。于是就有了,匿名对象->divA->闭包,闭包却没有引用diva,也就没有循环引用。


估计是因为闭包只引用函数变量(印象中,闭包是面向函数的编程时引入的概念),而this.divA实际上是leakTest的成员变量(实例变量),是外部可以访问的变量。
所以闭包并没有对this.divA的引用。
如果把上面的第二个代码改成:
 <div id="bb"><div id="aa">cc</div></div>     
 <script type="text/javascript">     
 function leakTest(){     
     var a=[];//用来加大闭包资源占用,方便观察     
     for(var i=0;i<100000;i++){     
      a.push('a');     
     }     
     this.divA=document.getElementById('aa');  
     var self = this.divA;
     this.divA.kk=function(){};     
     document.getElementById("bb").removeChild(this.divA);     
 }     
 new leakTest();  //产生个匿名对象   
 </script> 


这样闭包中有了对self的引用,因此就行成了this.divA对DOM节点( document.getElementById('aa') )的引用,而DOM节点又有对this.divA的引用,这就行成了循环引用(circular reference),资源无法回收。
36 楼 javascripteye 2009-02-05  
引用计数的gc方式是不大可能处理js对象和dom对象相互引用的,最好的处理方法是时刻保持清醒即时断开这种互引用就行了。
35 楼 likehibernate 2009-02-05  
这好像是IE的内存管理问题,好像在哪里看到,如果js与js对象互引用不会出现这种情况,如果js与native object互引用特别是用到闭包那这种内存泄露存在的可能性是极高的。
比较可笑的是有人一直去怪IE的内存管理而不去找到让其内存泄露的原因并改之!
34 楼 sutra 2008-08-22  
achun 写道
有深度呀,第一次看到这样的文章,长知识了。
顺手测试了一下www.jquery.com似乎同样有泄露,
不过我不知道,leaks栏中有红色的和绿色的差别是什么呀?
看来写js还要考虑leak问题呀!
做程序真.....的很...苦......
有泄露就.....要修...补......
啊.........

根据我的经验,红色表示你上次查看(show leaks)后新出现的leaks。
33 楼 trarck 2008-08-14  
请注意:
  这是楼主的代码,也是会产生内存泄露的代码:
  
<div id="bb"><div id="aa">cc</div></div>  
    <script type="text/javascript">  
    function leakTest(){  
        var a=[];//用来加大闭包资源占用,方便观察  
       for(var i=0;i<100000;i++){  
         a.push('a');  
        }  
       var divA=document.getElementById('aa');  
        divA.kk=function(){};  
       divA.parentNode.removeChild(divA);  
   }  
   leakTest();  
    </script>  

   这是liudaoru的代码,但没有产生内存泄露(不信你可以试试)
  
 <div id="bb"><div id="aa">cc</div></div>     
 <script type="text/javascript">     
 function leakTest(){     
     var a=[];//用来加大闭包资源占用,方便观察     
     for(var i=0;i<100000;i++){     
      a.push('a');     
     }     
     this.divA=document.getElementById('aa');     
     this.divA.kk=function(){};     
     document.getElementById("bb").removeChild(this.divA);     
 }     
 new leakTest();  //产生个匿名对象   
 </script> 


为什么呢?如此相似的代码,一个产生了泄露一个却没有。
个人观点:
楼主用一个var定义了个局部变量divA,闭包<=>div就有了循环引用。
liudaoru用this。this.divA没有被闭包占用(可能是this的特殊性)。于是就有了,匿名对象->divA->闭包,闭包却没有引用diva,也就没有循环引用。
32 楼 trarck 2008-08-14  
liudaoru 写道
那么这种情况该如何解释那?这种情况也没有太大的内存泄漏
<div id="bb"><div id="aa">cc</div></div>   
<script type="text/javascript">   
function leakTest(){   
    var a=[];//用来加大闭包资源占用,方便观察   
    for(var i=0;i<100000;i++){   
     a.push('a');   
    }   
    this.divA=document.getElementById('aa');   
    this.divA.kk=function(){};   
   	document.getElementById("bb").removeChild(this.divA);   
}   
new leakTest();   
</script>
 

你确定上面代码,IE下会产生内存泄露?
31 楼 Relucent 2008-07-07  
这么说遇到 IE 局部变量还是最后设 NULL比较安区
30 楼 soni 2008-06-25  
这属于IE的问题,关js鸟事。
29 楼 csf177 2008-06-25  
IE7疯了 没什么说的了
建议大家别试图处理IE7的内存泄露
28 楼 ericxu131 2008-06-24  
这个例子是不是就是说IE会强制释放可以访问的dom元素的资源
而divA.parentNode.removeChild(divA); 将divA这个所引用的div隐藏了,或者说设置为不可访问了,这个时候闭包里面引用的divA这个资源无法被释放,最终导致了内存泄漏??
27 楼 penghao122 2008-04-16  
收藏...竟自动回贴了.不好意思

26 楼 penghao122 2008-04-16  
oznyang 写道
在ie的内存泄露中跨页面的泄露是最严重的,浏览器刷新了仍然无法释放掉泄露占用的资源,造成访问速度越来越慢,内存占用越来越大
closure引起cross page leak的主要原因是closure和dom元素的互相引用
看这个例子:
<div id="bb"><div id="aa">cc</div></div>
<script type="text/javascript">
function leakTest(){
	var a=[];//用来加大闭包资源占用,方便观察
	for(var i=0;i<100000;i++){
	 a.push('a');
	}
	var divA=document.getElementById('aa');
	divA.kk=function(){};
	divA.parentNode.removeChild(divA);
}
leakTest();
</script>

    用sIEve看下发现这个页面每次刷新都会产生跨页面泄露,ie内存占用大了7MB,具体fins的文章中有过介绍
在这个例子中我们在leakTest()中创建了一个内部匿名函数并在dom元素aa的自定义属性kk中保存了他的引用,这就产生了一个闭包
divA.parentNode.removeChild(divA);
这句是产生泄露的主要原因,移除了aa并不会使这个节点消失,只不过在dom树上无法访问这个节点而已,由于闭包的存在divA这个局部变量不会被释放,而divA中保存着aa的引用,这就形成了一个循环引用,闭包保存了dom元素aa的引用,dom元素aa的自定义属性kk又保存了闭包内部的匿名函数的引用,所以在页面刷新的时候IE无法释放掉这个aa和闭包的资源,在我这个例子中就比较吓人,刷一下涨几MB内存
    让我们删掉divA.parentNode.removeChild(divA);这句试试,发现没有泄露发生
    我推测IE在刷新时会强行释放掉dom树上的元素,而不存在于dom树中的节点不会强行释放,所以造成了跨页面泄露,这是我的个人推测,有别的意见欢迎讨论
怎么解决这个问题呢,其实我们只要打断引用链就行了
<div id="bb"><div id="aa">cc</div></div>
<script type="text/javascript">
function leakTest(){
	var a=[];
	for(var i=0;i<100000;i++){
	 a.push('a');
	}
	var divA=document.getElementById('aa');
	divA.kk=function(){};
	divA.parentNode.removeChild(divA);
	divA=null;
}
leakTest();
</script>

或者
<div id="bb"><div id="aa">cc</div></div>
<script type="text/javascript">
function leakTest(){
	var a=[];
	for(var i=0;i<100000;i++){
	 a.push('a');
	}
	document.getElementById('aa').kk=function(){};
	document.getElementById('aa').parentNode.removeChild(document.getElementById('aa'));
	//这个例子不保存aa的应用,也不会引起泄露
}
leakTest();
</script>

or
<div id="bb"><div id="aa">cc</div></div>
<script type="text/javascript">
function leakTest(){
	var a=[];
	for(var i=0;i<100000;i++){
	 a.push('a');
	}
	var divA=document.getElementById('aa');
	divA.kk=function(){};
	divA.parentNode.removeChild(divA);
	return divA;
}
var divA=leakTest();
divA.kk=null; //这个可以看到内存占用比上面少了7MB,因为解除了对闭包内部函数的引用,闭包占用的资源被释放了
</script>

通过上面的例子可以看出,如果某个函数中dom元素保存了内部函数的引用,就会形成闭包,很容易引起泄露,务必小心
另firefox下测试是没有这些问题的


25 楼 笨笨狗 2008-04-15  
我从来不让循环链发生,所以这种问题还是可以避免的
24 楼 afcn0 2008-04-15  
什么情况下使用闭包?什么情况下都会有闭包,只不过是你想说是不是用匿名函数直接量把你想闭的闭进去还是不闭进去
23 楼 xieye 2008-04-15  
看了各位高手的解释,我似乎清楚,似乎又糊涂。
那这么说,闭包还是少用的好吗?

什么情况下可以方便使用闭包呢,我是一直都在大量使用闭包的。因为很方便。
困惑。
22 楼 hax 2008-04-14  
不要拿removeChild来做例子。因为这涉及另一个伪内存泄漏问题。事情要一桩一桩说,不要混在一起。
21 楼 afcn0 2008-04-14  
首先函数执行没有指明宿主情况,context是host也就是window,其实closure保存的是执行环境不是什么context,context一般情况下指函数执行过程中的this指向,应该问题就是楼上上说的从DOM无法回收,但是哪个元素的handler的closure里面还握着原来DOM的指针,也就是说浏览器刷新时候的remove和用js的remove不一样
20 楼 rainshow 2008-04-14  
afcn0 写道
rainshow 写道
hax 写道
divA.onclick=function(){};

在ie6中就已经leak了。

原因是闭包造成循环引用。根源还是循环引用。

divA 通过 onclick 属性引用了 闭包,这是显而易见的。
另一方面,闭包也引用了divA,虽然闭包没有显式引用divA。

下面的两块代码中没有闭包,或者闭包引用不到divA,就不会构成循环引用,因而不leak:

divA.onclick = new Function('');


function a() {};
new function () {
  var divA;
  ...
  divA.onclick = a;
}




"闭包没有显式引用divA"即存在隐性的引用
猜测:是否包含“闭包”的函数做为一个context被闭包引用着?
猜测:对于IE来说,不管你闭包有没有实际用到这个function里的变量,function本身作为一个context都已经被引用了,相当于divA.onclick=function a(this) {} ?

不要把context和closure混为一谈,和context没关系

呵呵,我说的context跟你说的不一样吧,我这里指的是闭包需要用到的一些环境(如函数)。我不知道这个context有什么还有其他意义。
19 楼 liudaoru 2008-04-14  
<p>关于cross-page leap,下面的文章讲的就比较清楚:</p>
<p><a href='http://www.javascriptkit.com/javatutors/closuresleak/index3.shtml'>http://www.javascriptkit.com/javatutors/closuresleak/index3.shtml</a></p>
<p>泄漏的根源不是闭包,这也是当没有divA.parentNode.removeChild(divA);是不存在泄漏的原因。问题的根源在于虽然js采用的是“标记-清除”回收策略,但是js和dom各自是单独回收的,而泄漏是有存在跨js和dom的循环引用造成的。</p>
<p>针对这个问题,通过removeChild将divA这个元素从dom树中摘除,所以dom的内存回收不会涉及到它;同时由于divA是局部变量,所以也无法从global对象到达。作为局部变量的divA因为相应的dom元素仍然存在,所以存在循环引用。而最重要的是,此时通过匿名函数divA与所在的scope相关联,进而导致整个leakTest中的变量均无法回收。这也是为什么使用this以后不存在问题的原因,同样将divA设为全局变量也不会有问题的原因。</p>
<p>divA.onclick=function(){}; 有问题的原因是其中隐含了对event的引用。</p>
<p>以上是我的理解,不当之处请大家指出。</p>
<p>下面是所引用文章中比较关键的文字:</p>
<p>...</p>
<p>Since the JScript garbage collector is a <a href='http://blogs.msdn.com/ericlippert/archive/2003/09/17/53038.aspx' target='_blank'>mark and sweep GC</a>, you may think that it would handle circular references. And in fact it does. However this circular reference is between the DOM and JS worlds. DOM and JS have separate garbage collectors. Therefore they cannot clean up memory in situations like the above.</p>
<p><strong>...</strong></p>
<p>The above pattern will leak due to closure. Here the closure's global variable <code>obj</code> is referring to the <strong>DOM</strong> element. In the mean time, the <strong>DOM</strong> element holds a reference to the entire closure. This generates a circular reference between the <strong>DOM</strong> and the <strong>JS</strong> worlds. That is the cause of leakage.</p>
<p>...</p>
18 楼 KKFC 2008-04-14  
太多逃逸的变量 不好.

相关推荐

    IE下的内存泄露问题解决方案(英文)

    本文将深入探讨IE下的内存泄露问题及其解决方案,并解析JavaScript闭包的多种用法,帮助开发者更好地理解和应对这一挑战。 ### IE内存泄露问题 #### 问题概述 在IE中,内存泄露主要是由于DOM元素与JavaScript对象...

    css样式和内存泄漏

    “JS关于ie的内存泄漏与javascript内存释放资料.doc”和“Js内存泄漏及解决方案.doc”可能详细解释了JavaScript中如何发生内存泄漏,尤其是在IE浏览器中的特殊情况。IE浏览器因为其独特的内存管理机制,如活动对象链...

    js内存泄露问题

    JavaScript内存泄露是一个重要的性能优化话题,尤其是在...对于IE浏览器,由于其早期版本的垃圾收集机制不完善,内存泄露问题更为突出,文档《理解并解决IE的内存泄漏方式》可能会提供更具体的针对IE浏览器的解决方案。

    深入浅出JavaScript内存泄漏.doc

    JavaScript内存泄漏是一个重要的主题,尤其是在现代Web开发中,因为页面长时间保持...在编写JavaScript代码时,应养成良好的编程习惯,如及时解除引用,谨慎使用闭包,以及正确处理跨页面引用,以减少内存泄漏的风险。

    JavaScript_内存泄露

    这种情况下,如果闭包和外部对象形成循环引用,内存泄露就可能发生。 例如: ```javascript function outer() { var obj = {}; function inner() { // 这里引用了 obj 对象 } obj.inner = inner; } ``` 在这...

    javascript 内存泄漏

    本文将详细探讨JavaScript内存泄漏的原因,并展示一些常见的内存泄漏模式,以及如何解决这些问题。 首先,JavaScript 是一种垃圾收集语言,意味着对象创建时会分配内存,在没有更多引用时由浏览器回收这些内存。...

    IE8 内存泄露(内存一直增长 )的原因及解决办法

    本文将深入探讨IE8内存泄露的原因以及相应的解决策略。 首先,内存泄露的根本原因在于IE8的一个已知bug。在IE8中,当创建某些特定类型的DOM(文档对象模型)节点,如form、button、input、select、textarea、a、img...

    js内存泄露的几种情况详细探讨

    3. **闭包引起的内存泄露**: 当函数内部引用了外部作用域的变量,尤其是引用了DOM对象时,会形成闭包,导致内存无法释放。比如给DOM元素添加事件监听器,内部函数引用了该元素,即使元素从DOM中移除,闭包依然存在...

    Angularjs memory leak in ie8 test

    这篇文章将深入探讨在IE8中AngularJS内存泄漏的成因、测试方法以及如何解决。 **1. AngularJS与IE8的兼容性** AngularJS 1.x版本在设计时并未充分考虑对IE8的全面支持,因为IE8并不完全支持ES5,而AngularJS依赖于...

    Javascript闭包(Closure)详解

    首先,闭包会导致函数中的变量长期保存在内存中,这可能会消耗大量内存资源,特别是在IE浏览器中,不恰当的使用可能会导致内存泄露。为了避免这种情况,我们应当在不需要的变量时,显式地将它们从内存中清除。 其次...

    2016前端面试题答案1

    然而,闭包也存在一定的缺点,由于它会保留对外部变量的引用,可能导致内存泄漏,特别是在长时间运行或大量使用闭包的应用中,需要注意内存管理。 闭包有三个关键特性: 1. 内部函数可以访问外部函数的变量。 2. ...

    最佳的addEvent事件绑定是怎样诞生的

    本文将深入探讨如何实现最佳的`addEvent`事件绑定方法,以及如何解决与之相关的内存泄漏和兼容性问题。 首先,让我们回顾一下传统事件绑定的方式,例如在给定的代码示例中,通过循环将`openClose`函数直接赋值给`H3...

    H5前端面试题,内含答案

    - **闭包**:闭包的原理及应用场景,避免内存泄漏的方法。 - **作用域**:变量的作用域链,函数的作用域规则。 - **事件机制**:DOM事件流、事件委托、事件捕获与冒泡等。 - **异步编程**:回调函数、Promise、async...

    关于图片的预加载过程中隐藏未知的

    第一,`img.onload`使用匿名函数,形成闭包,可能导致IE6等旧版浏览器的内存泄漏。因为闭包会保持对外部变量的引用,导致`img`对象无法被垃圾回收。为了解决这个问题,可以在`onload`事件触发后将`img.onload`设为`...

    JavaScript使用手册(1)

    定时器是实现动态效果和异步操作的重要工具,但需要注意的是,它们与浏览器的渲染循环相关联,可能导致阻塞或内存泄漏,因此在使用时需谨慎。 2. 图像对象: 在JavaScript中,我们可以使用`&lt;img&gt;`元素的`src`属性...

    面试js考察点

    ### 面试JS考察点详解 #### 1. 原型链 **定义**:JavaScript中的每个函数都有一个`...以上只是JavaScript面试中可能涉及的一些关键知识点,后续还将继续深入探讨其他方面,例如内存泄漏问题、长连接的应用等。

    前端面试题之工具类相关题集.zip

    - **Chrome DevTools**的使用:性能分析、内存泄漏检测、网络请求追踪等。 - **代码审计**:使用Webpack、Babel等工具进行代码压缩、优化。 8. **跨平台兼容性** - **浏览器差异**:了解不同浏览器的特性与兼容...

    浏览器事件动态注册和取消

    在本文中,我们将深入探讨如何动态地添加和移除事件监听器,以及这对网页性能和用户体验的影响。 首先,让我们了解事件处理的基本方式。在HTML中,我们可以通过属性来静态绑定事件,如`onclick`、`onmouseover`等。...

Global site tag (gtag.js) - Google Analytics