论坛首页 Web前端技术论坛

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

浏览 23968 次
该帖已经被评为良好帖
作者 正文
   发表时间:2008-04-13  
oznyang 写道
ls想说什么,我怎么没看懂呢
a这个数组只不过是为了便于观察泄露而已,你完全可以删掉


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

那你所说为什么能访问到a呢?其实闭包内存泄露很难完全避免的。
0 请登录后投票
   发表时间: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;
}



0 请登录后投票
   发表时间: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) {} ?
0 请登录后投票
   发表时间: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的引用。循环引用就此产生。
0 请登录后投票
   发表时间: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>
 
0 请登录后投票
   发表时间:2008-04-14  
我不知道是hax没有表达清楚还是你们理解不到位,function(){}这个一旦这么些,那么定义的执行环境就都保存下来了,就形成了交叉引用,解决办法就是把handler定义为外部单独引用函数对象或者直接new Function就可以直接把scope弄到window下面了,所以其他的没必要,不知道这些在ie6 ie7之间什么区别,谁有介绍,介绍介绍ie6 ie7区别
0 请登录后投票
   发表时间: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没关系
0 请登录后投票
   发表时间:2008-04-14  
太多逃逸的变量 不好.
0 请登录后投票
   发表时间:2008-04-14  

关于cross-page leap,下面的文章讲的就比较清楚:

http://www.javascriptkit.com/javatutors/closuresleak/index3.shtml

泄漏的根源不是闭包,这也是当没有divA.parentNode.removeChild(divA);是不存在泄漏的原因。问题的根源在于虽然js采用的是“标记-清除”回收策略,但是js和dom各自是单独回收的,而泄漏是有存在跨js和dom的循环引用造成的。

针对这个问题,通过removeChild将divA这个元素从dom树中摘除,所以dom的内存回收不会涉及到它;同时由于divA是局部变量,所以也无法从global对象到达。作为局部变量的divA因为相应的dom元素仍然存在,所以存在循环引用。而最重要的是,此时通过匿名函数divA与所在的scope相关联,进而导致整个leakTest中的变量均无法回收。这也是为什么使用this以后不存在问题的原因,同样将divA设为全局变量也不会有问题的原因。

divA.onclick=function(){}; 有问题的原因是其中隐含了对event的引用。

以上是我的理解,不当之处请大家指出。

下面是所引用文章中比较关键的文字:

...

Since the JScript garbage collector is a mark and sweep GC, 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.

...

The above pattern will leak due to closure. Here the closure's global variable obj is referring to the DOM element. In the mean time, the DOM element holds a reference to the entire closure. This generates a circular reference between the DOM and the JS worlds. That is the cause of leakage.

...

0 请登录后投票
   发表时间: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有什么还有其他意义。
0 请登录后投票
论坛首页 Web前端技术版

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