43

jQuery中導致內存泄漏的標準問題或編碼模式有哪些?jQuery內存泄漏模式和原因


我看到了一些與StackOverflow上的ajax()調用或jsonp或DOM刪除相關的問題。大多數jQuery內存泄漏問題都集中在特定問題或瀏覽器上,並且在jQuery中列出標準內存泄漏模式會很好。

這裏是SO一些相關的問題:

資源網站:

回答

28

據我瞭解,在JavaScript中的內存管理是由引用計數完成 - 而一個對象的引用仍然存在,也不會被釋放。這意味着在單個頁面應用程序中創建內存泄漏是微不足道的,並且可能導致來自java背景的那些內存泄漏。這不是特定於JQuery的。以下面的代碼爲例:

function MyObject = function(){ 
    var _this = this; 
    this.count = 0; 
    this.getAndIncrement = function(){ 
     _this.count++; 
     return _this.count; 
    } 
} 

for(var i = 0; i < 10000; i++){ 
    var obj = new MyObject(); 
    obj.getAndIncrement(); 
} 

在看內存使用情況之前,這看起來很正常。由於「_this」指針,MyObject的實例從不釋放,而頁面處於活動狀態(增加i的最大值以更顯着地看到它)。 (在老版本的IE中,它們從未被釋放,直到程序退出。)由於JavaScript對象可能在框架之間共享(我不建議嘗試這種方式,因爲它是嚴重的脾氣)。有時甚至在現代瀏覽器中javascript對象可能比他們想要的要長很多。

jQuery中的情況下,參照通常存儲保存DOM搜索的開銷 - 例如:由於

function run(){ 
    var domObjects = $(".myClass"); 
    domObjects.click(function(){ 
     domObjects.addClass(".myOtherClass"); 
    }); 
} 

此代碼將堅持以domObject(及其全部內容)永遠,在回調函數中引用它。

如果jquery的編寫者在內部錯過了這樣的實例,那麼庫本身就會泄漏,但更多的時候是客戶端代碼。

function run(){ 
    var domObjects = $(".myClass"); 
    domObjects.click(function(){ 
     if(domObjects){ 
      domObjects.addClass(".myOtherClass"); 
      domObjects = null; 
     } 
    }); 
} 

或再次執行查找:

function run(){ 
    $(".myClass").click(function(){ 
     $(".myClass").addClass(".myOtherClass"); 
    }); 
} 

一個好的經驗法則是要

第二個例子可以通過顯式清除當不再需要它的指針定注意定義回調函數的位置,並儘可能避免太多嵌套。

編輯:作爲由Erik的評論指出,你也可以使用this指針,以避免unnescessary DOM查找:

function run(){ 
    $(".myClass").click(function(){ 
     $(this).addClass(".myOtherClass"); 
    }); 
} 
+2

下面是一個例子:http://jsfiddle.net/qTu6y/8/您可以在探查器中使用Chrome的「Take a heapshot」來查看該代碼塊的每次運行都消耗大約20MB的RAM。 (在測試時,我確實碰到了「腳本在Chrome上使用了太多內存錯誤」) – Raynos

+0

對於「我們這些來自Java背景的人」......以及清晰的解釋。 – Thimmayya

+1

$(this).addClass在最後一種情況下性能會更好,因爲'this'代表一個核心JS DOM元素集合(這是JQuery對象通常使用適配器樣式模式包裝的東西),JQ不必在

15

我會貢獻一個反模式在這裏,這是「中鏈參考」泄漏。

一個jQuery的優勢之一是它的鏈接API,它可以讓你不斷地改變,過濾和操縱元素:

$(".message").addClass("unread").find(".author").addClass("noob"); 

在那個鏈的末端你有一個jQuery對象與所有的」。消息。作者「元素,但對象返回並與原始」.message「元素對象。您可以通過.end()方法得到他們,做一些對他們說:

$(".message") 
    .find(".author") 
    .addClass("prolific") 
    .end() 
    .addClass("unread"); 

現在使用這種方式時,有與泄漏沒有問題。但是,如果將鏈的結果賦予具有較長生命週期的變量,則對較早集的反向引用將保留在周圍,並且無法進行垃圾回收,直到該變量超出範圍。如果該變量是全局的,則引用永遠不會超出範圍。

例如,假設您在2008年的一篇博文中看到$("a").find("b")$("a b")「效率更高」(即使它不值得甚至考慮這樣的微優化)。你決定你需要一個全球性的,所以你這樣做是爲了保持作者列表頁寬:

authors = $(".message").find(".author"); 

現在你確實有作者列表jQuery對象,但它也指回的jQuery是消息的完整列表。你可能永遠不會使用它,甚至不知道它在那裏,它正在佔用內存。

注意泄漏只能與從現有的一組選擇元件的方法中發生,如.find.filter.children等文檔指示則返回集時。簡單地使用鏈接API不會導致泄漏,如果鏈具有簡單的非過濾方法等.css,所以這是好的:

authors = $(".message .author").addClass("prolific");