IE 内存泄露问题

 

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 οnlοad="SetupLeak()" οnunlοad="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.οnclick=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


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

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值