锁定老帖子 主题:IE下闭包引起跨页面内存泄露探讨
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-04-08
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下测试是没有这些问题的 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-04-08
有深度呀,第一次看到这样的文章,长知识了。
顺手测试了一下www.jquery.com似乎同样有泄露, 不过我不知道,leaks栏中有红色的和绿色的差别是什么呀? 看来写js还要考虑leak问题呀! 做程序真.....的很...苦...... 有泄露就.....要修...补...... 啊......... |
|
返回顶楼 | |
发表时间:2008-04-09
LZ对此问题果然分析仔细。 <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>
换成以上代码即可,不使用匿名函数,好像就没有泄漏。 可是我的代码中有很多的匿名函数呀。。。
|
|
返回顶楼 | |
发表时间:2008-04-09
我原文已经说过了,是闭包引起循环引用才会这样
你这个例子不会形成闭包的,自然不会泄露 |
|
返回顶楼 | |
发表时间:2008-04-09
原因其实很简单,DOM本身对象上有function(){}这样的事件函数很正常,事件函数有闭包也很正常,都不会泄露,唯一不正常的就是在事件函数闭包当中又以变量形式保存了本DOM元素的引用,那ie就比较郁闷了,不知道该怎么办了,所以解决办法有两个就是消灭变量或者把所有事件函数都null了,现在怎么都乱评新手帖呀,新手区又都不看,帮我评回来那个css问题
|
|
返回顶楼 | |
发表时间:2008-04-11
楼上没看仔细 divA.kk=function(){};
匿名函数里没有任何变量,可还是泄漏
|
|
返回顶楼 | |
发表时间:2008-04-11
我说的是"事件函数闭包当中又以变量形式保存了本DOM元素的引用",不是函数内部保存,函数外面哪个div只要不null,ie就是f5哪个内存也收不了
|
|
返回顶楼 | |
发表时间:2008-04-12
补充,还必须remove,不remove的话,ie7是可以在刷新以及浏览新页面清除所有DOM和事件函数,毫无问题
|
|
返回顶楼 | |
发表时间: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这些变量引用。关键是释放 “内部调用对象链”。 |
|
返回顶楼 | |
发表时间:2008-04-13
ls想说什么,我怎么没看懂呢
a这个数组只不过是为了便于观察泄露而已,你完全可以删掉 |
|
返回顶楼 | |