http://yiminghe.javaeye.com/blog/422434

1. ie 版本 <=6 循环引用出现:

 

Html代码 
  1. <html>  
  2. <head>  
  3.     <script type="text/javascript">  
  4.         var myGlobalObject;  
  5.         // 产生循环引用,因此会造成内存泄露  
  6.         function SetupLeak() {  
  7.   
  8.             //  First set up the script scope to element reference  
  9.             myGlobalObject = document.getElementById("LeakedDiv");  
  10.   
  11.             //  Next set up the element to script scope reference  
  12.             document.getElementById("LeakedDiv").expandoProperty = myGlobalObject;  
  13.   
  14.             // 或者通过事件属性注册形的闭包而造成的循环引用  
  15.             document.getElementById("LeakedDiv").onclick = function() {  
  16.   
  17.             };  
  18.   
  19.         }  
  20.   
  21.   
  22.         // 解开循环引用,解决内存泄露问题  
  23.         function BreakLeak() {  
  24.             return;  
  25.             document.getElementById("LeakedDiv").expandoProperty = null;  
  26.             document.getElementById("LeakedDiv").innerHTML = '';  
  27.             return confirm("ok?");  
  28.         }  
  29.     </script>  
  30. </head>  
  31.   
  32. <body onload="SetupLeak()" onunload="BreakLeak()">  
  33. <div id="LeakedDiv">xxxxxxxxx</div>  
  34. </body>  
  35. </html>  
 

 

上述代码展示了两种因为循环引用导致内存泄露的例子。

 

1. dom 节点的属性引用到自身节点,甚至包括不直接的引用,例如:

 

Js代码 
  1. (function(){  
  2. var d={b:document.body}  
  3. var obj={doc:d}; // ← obj.doc.b === document.body  
  4. document.body.o=obj; // ← Circular loop: document.body.o.doc.b === document.body  
  5. })();  
 

2. dom 节点的事件 handler 属性设置监听器函数,监听器函数的 [[scope]] 作用域会引用到该 dom 节点而造成隐蔽的循环引用。

 

Js代码 
  1. (function(){  
  2. var b=document.body; // ← create a reference to document.body inside of the outer scope.  
  3. b.onclick=function() { // ← b.onclick refers to a function.  
  4.   // this function can access "b" due to closure  
  5.   // do something...  
  6. };  
  7. })();  
 

注意事件使用 ie DOM2 的模式(attachEvent)注册事件时,由于没有直接和DOM属性接触,不会产生dom节点引用监听器函数,因而也不会产生循环引用,也不会带来内存泄露。

 

PS:类似 DOM,COM 在 ie 中也会因为循环引用而造成内存问题,譬如常见用于ajax 的 activeX:

 

Js代码 
  1. (function(){  
  2. var x=getXHRobject();  
  3. x.onreadystatechange=function() { // ← create link from x (COM object) to anonymous function  
  4.   if(x.readystate==4){ // ← reference to x exists inside function scope, creating circular link.  
  5.     // do something.  
  6.   
  7.     //消除循环引用  
  8.     x.onreadystatechange=null;  
  9.   }  
  10. };  
  11. })();  

 

解决:


ajax 的问题解决就比较简单了,只要在 onreadystatechange 调用函数中将这个属性清空就行了。


而对于 dom ,则建议是尽可能的从 javascript object 引用 dom object,而不要从 dom object 引用到 javascript object,如果一定要这样做的话,下面给出了一段解决代码,在页面 unload 时清楚已设置的属性。

 

Js代码 
  1. (function(){  
  2. var unLoaders=[];  
  3. myDomNode.object=new myObject(); // ← let’s say that this creates a leak somewhere  
  4. unLoaders.push(myDomNode); // ← save it for later  
  5. // create an “unload” function  
  6. var unload=function(){  
  7.   for(var i=unLoaders.length-1;i>-1;i–){  
  8.     unLoaders[i].object=null// ← break the cycle  
  9.   }  
  10. };  
  11. // run the unload function on window.unload  
  12. YAHOO.util.Event.addListener(window,’unload’,unload);  
  13. })();  
 

关键是:这个 bug 出现时即使刷新页面,内存仍然不会被释放 。 


这个bug 已经在 ie >=7 以及 xp sp3上修复。详见 Memory Leaks in Microsoft Internet Explorer 。

 

 

2. ie removeChild 时出现孤立节点问题


ie 下面清除一个节点,调用 dom removeChild 的方法会出现该节点的内存并没有真正释放,ie内存也没有减少,(但是刷新页面会释放),所以对于ie要调用 parent.innerHTML = ""; 的方法来强制释放内存。(另外在之前还要清除该元素的所有监听事件,否则dom有引用仍然是释放不了,ie 中 Jscript 和 Dom 是分开的)。


详见:extjs-3.0 Element.js remove 方法

 

Js代码 
  1. /** 
  2.          * Removes a DOM node from the document.  The body node will be ignored if passed in. 
  3.          * @param {HTMLElement} node The node to remove 
  4.          */  
  5.         removeNode : isIE ? function(){  
  6.             var d;  
  7.             return function(n){  
  8.                 if(n && n.tagName != 'BODY'){  
  9.                     d = d || DOC.createElement('div');  
  10.                     d.appendChild(n);  
  11.                     d.innerHTML = '';  
  12.                 }  
  13.             }  
  14.         }() : function(n){  
  15.             if(n && n.parentNode && n.tagName != 'BODY'){  
  16.                 n.parentNode.removeChild(n);  
  17.             }  
  18.         }  

 

总结:

 

一句话:尽量少使用 dom 自定义属性,尽量使用成熟类库注册事件。

 

参考资料:


对 ie 的内存缺陷算法感兴趣的话可以在以下文章中找到。


http://fins.javaeye.com/blog/172891


http://oznyang.javaeye.com/blog/180611?page=2


http://www.blogjava.net/tim-wu/archive/2006/05/29/48729.html


http://birdshome.cnblogs.com/archive/2005/02/16/104967.html

 

 

 

 

posted on 2010-09-28 23:25  星光~  阅读(849)  评论(0)    收藏  举报