`
oznyang
  • 浏览: 160959 次
  • 性别: 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下测试是没有这些问题的

分享到:
评论
17 楼 afcn0 2008-04-14  
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没关系
16 楼 afcn0 2008-04-14  
我不知道是hax没有表达清楚还是你们理解不到位,function(){}这个一旦这么些,那么定义的执行环境就都保存下来了,就形成了交叉引用,解决办法就是把handler定义为外部单独引用函数对象或者直接new Function就可以直接把scope弄到window下面了,所以其他的没必要,不知道这些在ie6 ie7之间什么区别,谁有介绍,介绍介绍ie6 ie7区别
15 楼 liudaoru 2008-04-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');   
    }   
    this.divA=document.getElementById('aa');   
    this.divA.kk=function(){};   
   	document.getElementById("bb").removeChild(this.divA);   
}   
new leakTest();   
</script>
 
14 楼 hax 2008-04-14  
不管是JScript还是SpiderMonkey或者其他js引擎,我上面举的例子中,闭包都引用到了divA。差别只是ie会因为循环引用而leak,而ff、opera、safari等不会因为循环引用而leak。

闭包为什么会引用divA?如果是
divA.onclick = function () { alert(divA) }
大家肯定能理解这里闭包引用了divA。

但是
divA.onclick = function () {}
函数空空如也这种情况呢?

没错,理论上来说,这个闭包没有引用任何外部变量,但是这一点是需要编译器优化才知道的。而现有的js引擎都不会做这个优化。而且就算有编译器优化,由于js的动态特性(eval和with)导致变量的resolve很有可能要到实际运行时才知道是否引用了某个外部变量,或者引用的是哪一个外部变量。

所以js引擎中,通常都是在创建闭包时,闭包会保存一个外部scope的引用(rhino中函数上有__parent__属性就是指向外层的scope引用),这个外部scope自然包括了divA的引用。循环引用就此产生。
13 楼 rainshow 2008-04-14  
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) {} ?
12 楼 hax 2008-04-14  
divA.onclick=function(){};

在ie6中就已经leak了。

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

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

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

divA.onclick = new Function('');


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



11 楼 radar 2008-04-13  
oznyang 写道
ls想说什么,我怎么没看懂呢
a这个数组只不过是为了便于观察泄露而已,你完全可以删掉


divA.onclick=function(){ alert(a)};

那你所说为什么能访问到a呢?其实闭包内存泄露很难完全避免的。
10 楼 oznyang 2008-04-13  
ls想说什么,我怎么没看懂呢
a这个数组只不过是为了便于观察泄露而已,你完全可以删掉
9 楼 radar 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这些变量引用。关键是释放 “内部调用对象链”。
8 楼 afcn0 2008-04-12  
补充,还必须remove,不remove的话,ie7是可以在刷新以及浏览新页面清除所有DOM和事件函数,毫无问题
7 楼 afcn0 2008-04-11  
我说的是"事件函数闭包当中又以变量形式保存了本DOM元素的引用",不是函数内部保存,函数外面哪个div只要不null,ie就是f5哪个内存也收不了
6 楼 xieye 2008-04-11  
<p>楼上没看仔细</p>
<pre name='code' class='html'> divA.kk=function(){};   </pre>
<p> </p>
<p>匿名函数里没有任何变量,可还是泄漏</p>
<p> </p>
5 楼 supercode 2008-04-09  
学习了,楼主研究的真深
4 楼 afcn0 2008-04-09  
原因其实很简单,DOM本身对象上有function(){}这样的事件函数很正常,事件函数有闭包也很正常,都不会泄露,唯一不正常的就是在事件函数闭包当中又以变量形式保存了本DOM元素的引用,那ie就比较郁闷了,不知道该怎么办了,所以解决办法有两个就是消灭变量或者把所有事件函数都null了,现在怎么都乱评新手帖呀,新手区又都不看,帮我评回来那个css问题
3 楼 oznyang 2008-04-09  
我原文已经说过了,是闭包引起循环引用才会这样
你这个例子不会形成闭包的,自然不会泄露
2 楼 xieye 2008-04-09  
<p>LZ对此问题果然分析仔细。<br/>偶在IE7下测试,结果确实。IE太奇怪。</p><pre name='code' class='html'>&lt;div id="bb"&gt;&lt;div id="aa"&gt;cc&lt;/div&gt;&lt;/div&gt;  
&lt;script type="text/javascript"&gt;  
function leakTest(){  
    var a=[];//用来加大闭包资源占用,方便观察  
    for(var i=0;i&lt;100000;i++){  
     a.push('a');  
    }  
    var divA=document.getElementById('aa');  
    divA.kk=ab;  
    divA.parentNode.removeChild(divA);  
}  
function ab(){}
leakTest();  
&lt;/script&gt; 
</pre><p> </p><p> </p><p>换成以上代码即可,不使用匿名函数,好像就没有泄漏。</p><p>可是我的代码中有很多的匿名函数呀。。。</p><p> </p>
1 楼 achun 2008-04-08  
有深度呀,第一次看到这样的文章,长知识了。
顺手测试了一下www.jquery.com似乎同样有泄露,
不过我不知道,leaks栏中有红色的和绿色的差别是什么呀?
看来写js还要考虑leak问题呀!
做程序真.....的很...苦......
有泄露就.....要修...补......
啊.........

相关推荐

    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