您的位置:首页 > 其它

理解并解决IE的内存泄漏方式[翻译3]

2009-11-18 13:17 309 查看
这一节非常的让人郁闷,不管是翻译过程还是文章内容本身。居然对DOM的这样简单操作区别都可以带来这么大的泄漏问题,真的是没有语言了。看完后欢迎说说你的感受,我的感受附在了文尾。

页面交叉泄漏(Cross-Page Leaks)

这种基于插入顺序而常常引起的泄漏问题,主要是由于对象创建过程中的临时对象未能被及时清理和释放造成的。它一般在动态创建页面元素,并将
其添加到页面DOM中时发生。一个最简单的示例场景是我们动态创建两个对象,并创建一个子元素和父元素间的临时域(译者注:这里的域(Scope)应该是
指管理元素之间层次结构关系的对象)。然后,当你将这两个父子结构元素构成的的树添加到页面DOM树中时,这两个元素将会继承页面DOM中的层次管理域对
象,并泄漏之前创建的那个临时域对象。下面的图示示例了两种动态创建并添加元素到页面DOM中的方法。在第一种方法中,我们将每个子元素添加到它的直接父
元素中,最后再将创建好的整棵子树添加到页面DOM中。当一些相关条件合适时,这种方法将会由于临时对象问题引起泄漏。在第二种方法中,我们自顶向下创建
动态元素,并使它们被创建后立即加入到页面DOM结构中去。由于每个被加入的元素继承了页面DOM中的结构域对象,我们不需要创建任何的临时域。这是避免
潜在内存泄漏发生的好方法。



Figure 3. DOM插入顺序泄漏模型

接下来,我们将给出一个躲避了大多数泄漏检测算法的泄漏示例。因为我们实际上没有泄漏任何可见的元素,并且由于被泄漏的对象太小从而你可能
根本不会注意这个问题。为了使我们的示例产生泄漏,在动态创建的元素结构中将不得不内联的包含一个脚本函数指针。在我们设置好这些元素间的相互隶属关系后
这将会使我们泄漏内部临时脚本对象。由于这个泄漏很小,我们不得不将示例执行成千上万次。事实上,一个对象的泄漏只有很少的字节。在运行示例并将浏览器导
航到一个空白页面,你将会看到两个版本代码在内存使用上的区别。当我们使用第一种方法,将子元素加入其父元素再将构成的子树加入页面DOM,我们的内存使
用量会有微小的上升。这就是一个交叉导航泄漏,只有当我们重新启动IE进程这些泄漏的内存才会被释放。如果你使用第二种方法将父元素加入页面DOM再将子
元素加入其父元素中,同样运行若干次后,你的内存使用量将不会再上升,这时你会发现你已经修复了交叉导航泄漏的问题。



<
html
>



<
head
>





<
script
language
="JScript"
>







function
LeakMemory()







{



var
hostElement
=
document.getElementById(
"
hostElement
"
);





//
Do it a lot, look at Task Manager for memory response





for
(i
=

0
; i
<

5000
; i
++
)







{



var
parentDiv
=



document.createElement(
"
<div onClick='foo()'>
"
);



var
childDiv
=



document.createElement(
"
<div onClick='foo()'>
"
);





//
This will leak a temporary object



parentDiv.appendChild(childDiv);



hostElement.appendChild(parentDiv);



hostElement.removeChild(parentDiv);



parentDiv.removeChild(childDiv);



parentDiv
=

null
;



childDiv
=

null
;



}



hostElement
=

null
;



}







function
CleanMemory()







{



var
hostElement
=
document.getElementById(
"
hostElement
"
);





//
Do it a lot, look at Task Manager for memory response





for
(i
=

0
; i
<

5000
; i
++
)







{



var
parentDiv
=



document.createElement(
"
<div onClick='foo()'>
"
);



var
childDiv
=



document.createElement(
"
<div onClick='foo()'>
"
);





//
Changing the order is important, this won't leak



hostElement.appendChild(parentDiv);



parentDiv.appendChild(childDiv);



hostElement.removeChild(parentDiv);



parentDiv.removeChild(childDiv);



parentDiv
=

null
;



childDiv
=

null
;



}



hostElement
=

null
;



}



</
script
>



</
head
>





<
body
>



<
button
onclick
="LeakMemory()"
>
Memory Leaking Insert
</
button
>



<
button
onclick
="CleanMemory()"
>
Clean Insert
</
button
>



<
div
id
="hostElement"
></
div
>



</
body
>



</
html
>

这类泄漏应该被澄清,因为这个解决方法有悖于我们在IE中的一些有益经验。创建带有脚本对象的DOM元素,以及它们已进行的相互关联是了解
这个泄漏的关键点。这实际上这对于泄漏来说是至关重要的,因为如果我们创建的DOM元素不包含任何的脚本对象,同时使用相同的方式将它们进行关联,我们是
不会有任何泄漏问题的。示例中给出的第二种技巧对于关联大的子树结构可能更有效(由于在那个示例中我们一共只有两个元素,所以建立一个和页面DOM不相关
的树结构并不会有什么效率问题)。第二个技巧是在创建元素的开始不关联任何的脚本对象,所以你可以安全的创建子树。当你把你的子树关联到页面DOM上后,
再继续处理你需要的脚本事件。牢记并遵守关于循环引用和闭包函数的使用规则,你不会再在挂接事件时在你的代码中遇到不同的泄漏。

我真的要指出这个问题,因为我们可以看出不是所有的内存泄漏都是可以很容易发现的。它们可能都是些微不足道的问题,但往往需要成千上万次的
执行一个更小的泄漏场景才能使问题显现出来,就像DOM元素插入顺序引起的问题那样。如果你觉得使用所谓的"最佳"经验来编程,那么你就可以高枕无忧,但
是这个示例让我们看到,即使是"最佳"经验似乎也可能带来泄漏。我们这里的解决方案希望能提高这些已有的好经验,或者介绍一些新经验使我们避免泄漏发生的
可能。

to be continued
...

不得不承认仔细的弄完这节的内容后,我有点被搞糊涂了。这个所谓的什么Cross-Page Leaks,是目前IE所有内存泄漏中我认为最严重的,前面两节说道的循环引用
闭包函数

是我们可以发现并合理避免的。这里先不说Cross-Page
Leaks到底有多少种场景,单示例给出的这个DOM插入顺序问题就够搞死人了。因为在使用Dhtml时,使用一个GetXxx、CreateXxx或
GenerateXxx函数来得到一个复杂的Html对象结构,再attach到需要的primary
DOM上是再平常不过的操作了,现在连这样都要带来Leak,真的是印证了这年头连萝卜都靠不住。之前看到过一个说法,由于IE的一些窗口之间有所有
(owner)关系,比如谁是谁的openner啥的,所以IE会保持一些navigate
away的页面内容,以便于被别的页面引用。可是不知道IE怎么搞得,连页面跳转到了别的domain或完全的空白(about:blank)页中后,所
占用的内存还是不能释放。这样严重的bug,微软还能美其名曰Cross-Page Leaks,真的是绝倒。其实只要能保证navigate to
other domain或navigate to blank
page后完全释放内存(这里根本都不需要GC,直接全部release就行了),其它的泄漏问题也就没有那么尖锐了,因为这时用户在不关闭IE的情况下
总还有个完全清理并回收内存的机会。那种运行了很久后,IE占用2,3百M内存(PM+VM)的情况就能大大的减小。不能回收内存的另一个莫名其妙的问题
是,这时脚本的引擎的效率会变得异常的低。话又说回来,现在的机器,都是512M或1G的内存,IE就是占用2,3百M内存,甚至3,4百M也不是什么不
能忍受的问题。可是这个时候,脚本引擎不知道怎么搞得,运行效率变得非常的低,似乎是每次GC脚本引擎都要把那些已leak的内存完全的sweep
and mark一遍,那个代价可想而知。当然这是一个猜想,否则也无法解释为什么脚本引擎的效率变得如此的低。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: