1. ie 版本 <=6 循环引用出现:
- <html>
- <head>
- <script type="text/javascript">
- var myGlobalObject;
- // 产生循环引用,因此会造成内存泄露
- function SetupLeak() {
- // First set up the script scope to element reference
- myGlobalObject = document.getElementById("LeakedDiv");
- // Next set up the element to script scope reference
- document.getElementById("LeakedDiv").expandoProperty = myGlobalObject;
- // 或者通过事件属性注册形的闭包而造成的循环引用
- document.getElementById("LeakedDiv").onclick = function() {
- };
- }
- // 解开循环引用,解决内存泄露问题
- function BreakLeak() {
- return;
- document.getElementById("LeakedDiv").expandoProperty = null;
- document.getElementById("LeakedDiv").innerHTML = '';
- return confirm("ok?");
- }
- </script>
- </head>
- <body onload="SetupLeak()" onunload="BreakLeak()">
- <div id="LeakedDiv">xxxxxxxxx</div>
- </body>
- </html>
<html>
<head>
<script type="text/javascript">
var myGlobalObject;
// 产生循环引用,因此会造成内存泄露
function SetupLeak() {
// First set up the script scope to element reference
myGlobalObject = document.getElementById("LeakedDiv");
// Next set up the element to script scope reference
document.getElementById("LeakedDiv").expandoProperty = myGlobalObject;
// 或者通过事件属性注册形的闭包而造成的循环引用
document.getElementById("LeakedDiv").onclick = function() {
};
}
// 解开循环引用,解决内存泄露问题
function BreakLeak() {
return;
document.getElementById("LeakedDiv").expandoProperty = null;
document.getElementById("LeakedDiv").innerHTML = '';
return confirm("ok?");
}
</script>
</head>
<body onload="SetupLeak()" onunload="BreakLeak()">
<div id="LeakedDiv">xxxxxxxxx</div>
</body>
</html>
上述代码展示了两种因为循环引用导致内存泄露的例子。
1. dom 节点的属性引用到自身节点,甚至包括不直接的引用,例如:
- (function(){
- var d={b:document.body}
- var obj={doc:d}; // ← obj.doc.b === document.body
- document.body.o=obj; // ← Circular loop: document.body.o.doc.b === document.body
- })();
2. dom 节点的事件 handler 属性设置监听器函数,监听器函数的 [[scope]] 作用域会引用到该 dom 节点而造成隐蔽的循环引用。
- (function(){
- var b=document.body; // ← create a reference to document.body inside of the outer scope.
- b.onclick=function() { // ← b.onclick refers to a function.
- // this function can access "b" due to closure
- // do something...
- };
- })();
注意事件使用 ie DOM2 的模式(attachEvent)注册事件时,由于没有直接和DOM属性接触,不会产生dom节点引用监听器函数,因而也不会产生循环引用,也不会带来内存泄露。
PS:类似 DOM,COM 在 ie 中也会因为循环引用而造成内存问题,譬如常见用于ajax 的 activeX:
- (function(){
- var x=getXHRobject();
- x.onreadystatechange=function() { // ← create link from x (COM object) to anonymous function
- if(x.readystate==4){ // ← reference to x exists inside function scope, creating circular link.
- // do something.
- //消除循环引用
- x.onreadystatechange=null;
- }
- };
- })();
解决:
ajax 的问题解决就比较简单了,只要在 onreadystatechange 调用函数中将这个属性清空就行了。
而对于 dom ,则建议是尽可能的从 javascript object 引用 dom object,而不要从 dom object 引用到 javascript object,如果一定要这样做的话,下面给出了一段解决代码,在页面 unload 时清楚已设置的属性。
- (function(){
- var unLoaders=[];
- myDomNode.object=new myObject(); // ← let’s say that this creates a leak somewhere
- unLoaders.push(myDomNode); // ← save it for later
- // create an “unload” function
- var unload=function(){
- for(var i=unLoaders.length-1;i>-1;i–){
- unLoaders[i].object=null; // ← break the cycle
- }
- };
- // run the unload function on window.unload
- YAHOO.util.Event.addListener(window,’unload’,unload);
- })();
关键是:这个 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 方法
- /**
- * Removes a DOM node from the document. The body node will be ignored if passed in.
- * @param {HTMLElement} node The node to remove
- */
- removeNode : isIE ? function(){
- var d;
- return function(n){
- if(n && n.tagName != 'BODY'){
- d = d || DOC.createElement('div');
- d.appendChild(n);
- d.innerHTML = '';
- }
- }
- }() : function(n){
- if(n && n.parentNode && n.tagName != 'BODY'){
- n.parentNode.removeChild(n);
- }
- }
总结:
一句话:尽量少使用 dom 自定义属性,尽量使用成熟类库注册事件。
参考资料:
对 ie 的内存缺陷算法感兴趣的话可以在以下文章中找到。
http://fins.iteye.com/blog/172891
http://oznyang.iteye.com/blog/180611?page=2
http://www.blogjava.net/tim-wu/archive/2006/05/29/48729.html