2014-06-25 71 views
2

我正在閱讀這篇關於javascript優化的文章。 Articlejavascript間隔內存泄漏

我遇到了這個部分,它告訴我什麼時候發生泄漏。但我無法找到稱呼它的正確方法,因此不會發生泄漏。這裏是我感興趣的部分。

泄漏最糟糕的地方之一是在循環或setTimeout()/ setInterval(),但這是相當普遍的。 考慮下面的例子。

var myObj = { 
    callMeMaybe: function() { 
     var myRef = this; 
     var val = setTimeout(function() { 
      console.log('Time is running out!'); 
      myRef.callMeMaybe(); 
     }, 1000); 
    } 
}; 

如果我們再運行:

myObj.callMeMaybe(); 

開始計時,我們可以看到每一秒「的時間已經不多了!」如果我們再運行:

myObj = null; 

計時器仍然會啓動。 myObj不會被垃圾收集,因爲傳遞給setTimeout的閉包必須保持活動才能執行。反過來,它保存對myObj的引用,因爲它捕獲了myRef。如果我們將閉包傳遞給任何其他函數,並保持對它的引用,這將是相同的。

值得一提的是,setTimeout/setInterval調用中的引用(如函數)需要在垃圾收集之前執行並完成。

問題是:你如何正確地做到這一點,使你不泄漏?這與調用clearInterval一樣簡單嗎?這是否泄漏一次或每間隔泄漏一次

+0

請記住'val'在函數外部不可用,所以您需要一些工廠來管理應用程序中的所有計時器。 要調用clearInterval,您需要知道'val'的值 –

回答

1

我不會以任何方式稱這是內存泄漏 - 這是簡單的垃圾回收做它應該的。沒有更多「正確」的方式來做到這一點。

只要您的計時器正在運行,最初由myObj指向的對象仍在使用中。只要沒有更多的引用,垃圾收集器就會釋放它。設置myObj = null會清除對它的一個引用,但您的正在進行的定時器在myRef中有另一個引用,因此只有在引用它的所有引用都消失後才能進行垃圾收集。

是的,如果你停止你的計時器並設置myObj = null,那麼將不會有更多的對象引用,GC將擺脫它。請記住,如果您想從外部停止計時器,則需要提供時間表的訪問權限,因爲沒有外部代碼可以到達val,您現在已經存儲了該計時器。


如果你有很多其他數據的myObj計時器並不需要訪問和你試圖允許在定時器繼續運行數據被釋放,那麼你就可以保持相同的結構您現在已經清除了該對象的其他數據(可以刪除屬性或將屬性設置爲null),也可以更改代碼的結構,以便重複定時器可以通過單獨的函數調用啓動,但不具有保持對該對象的引用以調用方法。

換句話說,如果您的計時器方法需要訪問該對象,則該對象通過垃圾回收器正確保持活動狀態。如果計時器方法不需要訪問該對象,那麼您應該通過其他方式運行重複計時器,這樣不會重複調用對象的方法 - 從而允許對象進行垃圾回收。

+0

您是否必須顯式調用clearTimer或在setTimeout的情況下是否自動清除? – Leo

+0

@Leo:你可以設置多個超時設置,所以'setTimeout'不會與已經設置的那個混淆。你必須調用'clearTimeout'。或者,您可以檢查'myObj'是否等於'myRef',並且在這種情況下不需要再次調用'setTimeout'。 (你應該可以通過閉包訪問'myObj')。但是請注意,如果你這樣做,那麼如果你想要額外的超時,你必須將它存儲在一個不同的變量中。 – cHao

+1

@Leo:回調一旦被觸發就會被釋放(當然,除非你通過再次調用'setTimeout'來重新附加它)。所以如果這是唯一持有引用的東西,它會變成垃圾。 – cHao