node.js中的模塊只是一個閉包,它依靠與Javascript中的其他閉包保持活動相同的規則保持活動狀態。只要閉包中的任何代碼仍然可以被其他代碼訪問,那麼閉包本身就不能被垃圾收集。而且,模塊也可以通過模塊加載器進行緩存,這意味着即使沒有其他代碼保留對模塊的引用,模塊的引用也會保留在模塊緩存中。您可能會發現閱讀這篇文章很有幫助:How require() Actually Works,因爲它是一個範圍的正常的Javascript GC,它決定了一個給定的模塊何時可以被垃圾回收。沒有特殊的垃圾收集模塊。
因此,只要在模塊加載時創建的模塊閉包保持引用,您的模塊變量就會處於活動狀態。當該閉包處於活動狀態時,在模塊加載時模塊內引用變量的內容將被釋放的唯一方式是手動清除這些變量的內容(例如,設置爲null
)。
默認情況下,即使節點模塊當前不再被其他代碼使用或引用,節點模塊仍保持活動狀態並加載到節點模塊高速緩存中(等待其它代碼再次訪問require()
)。如果您願意,可以從緩存中手動刪除它。請參閱此答案以獲取有關從緩存中手動刪除模塊(以及可能加載的任何模塊)的詳細信息:Unloading node code/modules。因此,如果您的代碼中沒有任何對模塊的引用,則模塊中沒有實時事件處理程序或回調,並且您已從緩存中手動刪除模塊,則模塊封閉不應再有任何可達到的代碼引用它和GC可以釋放整個範圍(以及模塊)。
只要其中的任何代碼仍然可以訪問(例如,仍然可以被任何其他代碼調用),模塊就會保持活動狀態。在你的情況下,如果任何其他代碼仍然有對模塊的引用,或者只要模塊中的事件處理程序仍然存在(仍然可以被調用),就會發生這種情況,因爲事件處理程序回調引用了模塊中的代碼。並不總是清楚給定的垃圾收集器在知道某個給定的事件處理程序何時完成並且將來永遠不會再被調用時的智能程度。
在你的具體的例子,$(window).scroll()
事件處理程序(這似乎有點像一個由例子,因爲通常有沒有window
對象,它的滾動的node.js),理論上是永遠活着,直到你手動刪除的事件處理程序與.off()
或直到window
對象本身被刪除或類似的東西。因此,該事件處理程序內的引用永遠不會自行消失。
當ajax調用本身完成執行並調用了所有回調函數時,將完成具有特定生命週期的其他事件處理程序,如Ajax成功處理程序。那些事件處理程序將釋放他們完成時所持有的任何引用。相同的setTimeout()
。它將釋放它在執行時保存的任何引用(或者取消定時器時)。
通常很難預測垃圾收集器的智能程度如何,以及何時會意識到給定變量不再可達,因此可以進行垃圾收集。有些事情很容易理解,例如當一個變量超出範圍,並且沒有其他引用留在範圍內,因此整個範圍將被GCed。但有些事情並不那麼簡單,例如當範圍仍然存活時,因爲該範圍中的事件處理程序仍然存在,但該特定事件處理程序中沒有任何內容可以實際引用該範圍內的給定變量。在這種情況下,GC是否會真正嘗試在該範圍內GC單個變量是依賴於實現的。像eval()
和構建新的Function
對象與通過字符串操作構建的代碼使得它非常難以確切地知道將來可能和不可能從給定的事件處理程序或回調引用什麼(因爲可以通過編程構造幾乎任何引用沒有口譯員知道你將來可能會提到什麼)。這使細粒度垃圾收集變得複雜。你可以指望的是整個範圍的垃圾收集(當整個範圍被釋放時)。在細粒度的GC上計算比它可能不明智。如果你在一個可能會持續很長時間的範圍內使用一個非常大的變量來完成顯式操作,那麼只需要將這個非常大的變量放在null
之外就可以了,所以當你希望清除大數據時,它的特定引用將被清除。這對於一個小字符串來說並不重要(除非你有成千上萬的這些對象),但可能與大緩衝區或非常大的字符串有關。
編輯:但看來V8做一個範圍內的各個變量的垃圾收集,如果這些變量本身不在任何這仍然是封閉內到達代碼的引用,沒有任何用途eval()
在相同的代碼。我還沒有找到關於這個問題的權威性參考文獻,但已經證實,這似乎是在測試實際情況時的情況。
@MarcoBonelli - 可能的重複只是一個通用的垃圾回收問題/答案,並沒有解決或解釋這個問題在node.js中關於模塊級變量的問題。 – jfriend00
我編輯了這個問題。另外,我沒有說,但它是隱含的。我希望在這個特定的情況下做出具體迴應,而不是GC理論中的通用複製粘貼迴應。這將是一個很好的解釋時間表。另外不要忘記事件處理程序的生命。他們影響這個嗎? – Adminiculo