论坛首页 Web前端技术论坛

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

浏览 23522 次
该帖已经被评为良好帖
作者 正文
   发表时间:2008-04-08  
在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下测试是没有这些问题的

   发表时间:2008-04-08  
有深度呀,第一次看到这样的文章,长知识了。
顺手测试了一下www.jquery.com似乎同样有泄露,
不过我不知道,leaks栏中有红色的和绿色的差别是什么呀?
看来写js还要考虑leak问题呀!
做程序真.....的很...苦......
有泄露就.....要修...补......
啊.........
0 请登录后投票
   发表时间:2008-04-09  

LZ对此问题果然分析仔细。
偶在IE7下测试,结果确实。IE太奇怪。

<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=ab;   
    divA.parentNode.removeChild(divA);   
}   
function ab(){}
leakTest();   
</script>  

 

 

换成以上代码即可,不使用匿名函数,好像就没有泄漏。

可是我的代码中有很多的匿名函数呀。。。

 

0 请登录后投票
   发表时间:2008-04-09  
我原文已经说过了,是闭包引起循环引用才会这样
你这个例子不会形成闭包的,自然不会泄露
0 请登录后投票
   发表时间:2008-04-09  
原因其实很简单,DOM本身对象上有function(){}这样的事件函数很正常,事件函数有闭包也很正常,都不会泄露,唯一不正常的就是在事件函数闭包当中又以变量形式保存了本DOM元素的引用,那ie就比较郁闷了,不知道该怎么办了,所以解决办法有两个就是消灭变量或者把所有事件函数都null了,现在怎么都乱评新手帖呀,新手区又都不看,帮我评回来那个css问题
0 请登录后投票
   发表时间:2008-04-11  

楼上没看仔细

 divA.kk=function(){};   

 

匿名函数里没有任何变量,可还是泄漏

 

0 请登录后投票
   发表时间:2008-04-11  
我说的是"事件函数闭包当中又以变量形式保存了本DOM元素的引用",不是函数内部保存,函数外面哪个div只要不null,ie就是f5哪个内存也收不了
0 请登录后投票
   发表时间:2008-04-12  
补充,还必须remove,不remove的话,ie7是可以在刷新以及浏览新页面清除所有DOM和事件函数,毫无问题
0 请登录后投票
   发表时间:2008-04-13  
  
 1. <div id="bb"><div id="aa">cc</div></div>  
   2. <script type="text/javascript">  
   3. function leakTest(){  
   4.     var a=[];//用来加大闭包资源占用,方便观察  
   5.     for(var i=0;i<100000;i++){  
   6.      a.push('a');  
   7.     }  
   8.     var divA=document.getElementById('aa');  
   9.     divA.kk=function(){};  
  10.     divA.parentNode.removeChild(divA);  
  11. }  
  12. leakTest();  
  13. </script> 
 

大致看了一下,你们都注意到“closure和dom元素的互相引用 ”。
我觉得你们都错了。
----------------------------------
如果我改成
divA.kk=function(){ alert(a)};
你们觉得会运行吗?答案是会的。引起这个大的内存占用是 a=[] 这个数组,而不是什么相互应用!
闭包会形成个“内部调用对象链”,保存着类似aa divA这些变量引用。关键是释放 “内部调用对象链”。
0 请登录后投票
   发表时间:2008-04-13  
ls想说什么,我怎么没看懂呢
a这个数组只不过是为了便于观察泄露而已,你完全可以删掉
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics