2010-08-03 87 views
15

許多文章(例如msdn)都表示,某些瀏覽器無法清除循環引用,因爲它涉及到DOM對象和JS對象。JQuery垃圾收集 - 這會乾淨嗎?

(IE 6不能做它在所有和IE7只能頁面請求之間做):

的Javascript原生(泄漏):

function leak(){ 
    var elem = document.createElement("DIV"); 
    document.body.appendChild(elem); 
    elem.onclick = function() { 
     elem.innerHTML = elem.innerHTML + "."; 
     // ... 
    }; 
} 

因爲元素的onload屬性是指通過一個閉合回自身,它創建一個循環引用

elem [DOM] -> elem.onclick [JS] -> elem [DOM]

JQuery的版本(不漏):

function leak(){ 
    var elem = $('<div></div>'); 
    $(document.body).append(elem); 
    elem.click(function() { 
     elem.html(elem.html() + "."); 
     // ... 
    }; 
} 

在這種情況下,jQuery的停止泄漏在甚至涉及所有瀏覽器發生,雖然仍有循環引用:

elem [JS] -> element [DOM] -> elem.onclick [JS] -> elem [JS]

我的問題:如果仍有循環引用,jQuery如何停止泄漏?

回答

6

在jQuery代碼的最後一步(用於灒代碼之前,它的選擇器引擎)是這個(這是代碼,以防止泄漏):

// Prevent memory leaks in IE 
// Window isn't included so as not to unbind existing unload events 
// More info: 
// - http://isaacschlueter.com/2006/10/msie-memory-leaks/ 
if (window.attachEvent && !window.addEventListener) { 
    window.attachEvent("onunload", function() { 
     for (var id in jQuery.cache) { 
      if (jQuery.cache[ id ].handle) { 
       // Try/Catch is to handle iframes being unloaded, see #4280 
       try { 
        jQuery.event.remove(jQuery.cache[ id ].handle.elem ); 
       } catch(e) {} 
      } 
     } 
    }); 
} 

當你在jQuery的做任何事情,它存儲它所做的(即函數)和什麼(即DOM元素)。 onunload通過jQuery緩存從其內部緩存的事件處理程序中刪除函數(這是事件存儲的位置,而不是單個DOM節點上)。

哦,行:

if (window.attachEvent && !window.addEventListener) { 

確保它只是運行在IE瀏覽器。

+0

因此,它刪除元素並打破IE6/6的循環引用。感謝您的洞察力 – 2010-08-11 12:21:57

2

JQuery只能確保在通過庫進行所有操作時不會發生泄漏。在jQuery中有一些名爲「empty」和「cleanData」的例程,您可以仔細查看究竟發生了什麼,但基本上這些代碼在釋放它們之前只是從DOM元素中分離出它所知道的所有信息。當你使用「.html()」或「.load()」來覆蓋元素內容時,就會調用這個例程。

就我個人而言,我很擔心像這樣的情況下的「保證」。

+0

我知道當你刪除一個元素時,它會清除所有的東西(所以我的例子並不是一個完美的例子),但是例如在IE6中,如果在導航到下一頁時存在循環引用,則內存將泄漏(當你不刪除元素)。 – 2010-08-03 12:41:59

+0

啊,我明白你在說什麼了。那麼,另一種jQuery幫助的方式是避免直接將任何*鉤到DOM節點上。 – Pointy 2010-08-03 12:53:33

+0

它並沒有涉及到它,但從技術上講,它將自己綁定到它,因此它將所有的jQuery事件和數據都綁定到它上面(當你調用.remove時可以正確清理它們)。 – 2010-08-03 13:00:25

1

改寫爲進一步澄清

實際上,有2個原因在所提供的示例內存泄漏。第一個內存泄漏是由於創建了一個對閉包內的活動DOM節點的引用而引起的直接。傳統瀏覽器(如IE6)的垃圾收集器(JS & DOM)無法取消這些引用。因此需要在函數結束時清空節點引用。

由於活動DOM元素被附加到jQuery對象作爲屬性/屬性,jQuery默認避開了這種情況,前面提到的垃圾收集器在確定空引用時沒有問題。如果jQuery對象具有空引用,它將被簡單地清除,並且它的屬性/屬性(在這種情況下是對活DOM元素的引用)。

所以爲了避免這種內存泄漏,是讓對象維護對活動DOM節點的引用,然後引用封閉對象中的對象。閉包將只保留對該對象的引用,而不會保留活動DOM元素,因爲該引用屬於該對象。

// will still leak, but not due to closure references, thats solved. 
function noLeak(){ 
    var obj= { 
     elem: document.createElement('div') 
    } 
    obj.elem.onclick = function(){ 
     obj.elem.innerHTML = obj.elem.innerHTML + "."; 
    } 
} 

這清除了最明顯的循環引用,但仍然存在泄漏(onclick)。該節點具有對引用該對象的函數的引用,該對象又引用該節點。解決這個漏洞的唯一方法就是從addEvent比賽中學習(很多人努力解決這個漏洞;))。 Coincidentaly,需要的代碼可以在其中找到,所以我的應用程序不提供代碼;)

爲事件系統創建包裝會添加更多的代碼,但是非常重要。主要想法是添加一個通用的eventHandler,將事件委託給存儲所需引用的事件緩存/系統。在卸載事件中,緩存清除了循環引用,允許垃圾收集器(JS和DOM)整理自己的角落。

+0

所有的jQuery對象都有對它們操作的DOM元素的引用,所以技術元素 - > DOMELEMENT - > onload函數 - > elem。所以有一個循環參考發生。而在我真正的JS我需要使用閉包,所以我不能無效elem。 – 2010-08-03 12:50:21

+0

「A」循環引用不是內存泄漏的原因;)原因是活動DOM元素的「THE」循環引用。見上面的附錄。 – BGerrissen 2010-08-10 22:17:54

+0

jQuery內部仍然有一個DOM元素的引用,即使事件/數據沒有被賦值給DOM元素,仍然存在循環引用。 - 儘管最後一句(在答案中)使我感興趣 – 2010-08-11 12:23:13