JavaScript內存泄漏

1、什麼是閉包、以及閉包所涉及的作用域鏈這裏就不說了。

2、JavaScript垃圾回收機制

JavaScript不需要手動地釋放內存,它使用一種自動垃圾回收機制(garbage collection)。當一個對象無用的時候,即程序中無變量引用這個對象時,就會從內存中釋放掉這個變量。

var s = [ 1, 2 ,3];
var s = null;
//這樣原始的數組[1 ,2 ,3]就會被釋放掉了。

3、循環引用

三個對象 A 、B 、C

AàBàC :A的某一屬性引用着B,同樣C也被B的屬性引用着。如果將A清除,那麼B、C也被釋放。

AàBàCàB :這裏增加了C的某一屬性引用B對象,如果這是清除A,那麼B、C不會被釋放,因爲B和C之間產生了循環引用。

var a = {};
a.pro = { a:100 };
a.pro.pro = { b:100 };
a = null ; 
//這種情況下,{a:100}和{b:100}就同時也被釋放了。
            
var obj = {};
obj.pro = { a : 100 };
obj.pro.pro = { b : 200 };
var two = obj.pro.pro;
obj = null;    
//這種情況下 {b:200}不會被釋放掉,而{a:100}被釋放了。

4、循環引用和閉包

function outer(){
    var obj = {};
    function inner(){ 
        //這裏引用了obj對象
    }
    obj.inner = inner;
}

這是一種及其隱蔽的循環引用,。當調用一次outer時,就會在其內部創建obj和inner兩個對象,obj的inner屬性引用了inner;同樣inner也引用了obj,這是因爲obj仍然在innerFun的封閉環境中,準確的講這是由於JavaScript特有的“作用域鏈”。
因此,閉包非常容易創建循環引用,幸運的是JavaScript能夠很好的處理這種循環引用。

5、IE中的內存泄漏

IE中的內存泄漏有好幾種,這裏有詳細的解釋(http://msdn.microsoft.com/en-us/library/bb250448.aspx),園子裏也有翻譯了(http://www.cnblogs.com/birdshome/archive/2006/05/28/ie_memoryleak.html)。

這裏只討論其中一種,即循環引用所造成的內存泄漏,因爲,這是一種最普遍的情況。

當在DOM元素或一個ActiveX對象與普通JavaScript對象之間存在循環引用時,IE在釋放這類變量時存在特殊的困難,最好手動切斷循環引用,這個bug在IE 7中已經被修復了(http://www.quirksmode.org/blog/archives/2006/04/ie_7_and_javasc.html)。

“IE 6 suffered from memory leaks when a circular reference between several objects, among which at least one DOM node, was created. This problem has been solved in IE 7. ”


如果上面的例子(第4點)中obj引用的不是一個JavaScript Function對象(inner),而是一個ActiveX對象或Dom元素,這樣在IE中所形成的循環引用無法得到釋放。

function init(){
	var elem = document.getElementByid( 'id' );
	elem.onclick = function(){
		alert('rain-man');
		//這裏引用了elem元素
	};
}

Elem引用了它的click事件的監聽函數,同樣該函數通過其作用域鏈也引用回了elem元素。這樣在IE中即使離開當前頁面也不會釋放這些循環引用。

6、解決方法

基本的方法就是手動清除這種循環引用,下面一個十分簡單的例子,實際應用時可以自己構建一個addEvent()函數,並且在window的unload事件上對所有事件綁定進行清除。

    function outer(){
        var one = document.getElementById( 'one' );
        one.onclick = function(){};
    }
    window.onunload = function(){
        var one = document.getElementById( 'one' );
        one.onclick = null;
    };

其它方法(by:Douglas Crockford

/**
 * 遍歷某一元素節點及其所有後代元素
 *
 * @param Elem node  所要清除的元素節點
 * @param function func  進行處理的函數
 * 
 */
function walkTheDOM(node, func) {
    func(node); 
    node = node.firstChild; 
    while (node) { 
        walkTheDOM(node, func); 
        node =  node.nextSibling; 
    } 
} 
/**
 * 清除dom節點的所有引用,防止內存泄露
 *
 * @param Elem node  所要清除的元素節點
 * 
 */
function purgeEventHandlers(node) {
	walkTheDOM(node, function (e) {
		for (var n in e) {            
			if (typeof e[n] === 'function') {
				e[n] = null;
			}
		}
	});
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章