C#學習筆記-記憶體回收機制
羅朝輝(http://www.cnblogs.com/kesalin/)
《C#與.NET進階程式設計》讀書筆記
1,C#的記憶體回收機制
C#的記憶體回收機制不是基於引用計數的,而是基於對象是否可到達。該機制的運作過程是:CLR 會建立一個對象圖,代表堆上可達的每一個對象,如果在一次記憶體回收過程中,某個對象在該對象圖上沒有root(即沒有任何其他對象依賴於它),則對象是不可達對象,會被標記為垃圾,從而會被終結,從記憶體中清除。 微軟的回收演算法使用對象代,用於終結對象的輔助線程和專門承載大對象的託管堆進行最佳化以提高記憶體回收機制的效率。
2,終結過程
Finalize() 的作用是保證.NET對象能在記憶體回收時清除非託管資源。如果建立了一個不使用非託管實體的類型,終結是沒有用的。事實上,儘可能在設計時避免提供Finalize()以提高效率,因為終結是需要花費時間的。當在託管堆上指派至時,運行庫自動確定該對象是否提供一個自訂的Finalize()方。如果是這樣的話,該對象就被標記為可終結的,同時一個指向該對象的指標被儲存至名為終結隊列的內部隊列中。終結隊列是一個由記憶體回收行程維護的表,它指向每一個在從堆上刪除之前必須被終結的對象。
當記憶體回收行程確定需要從記憶體中釋放一個對象時,它會檢查終結隊列中的每一項,並將對象從堆上複製到另一個稱作終結可達表的託管結構上。此時,下一個記憶體回收時將產生另一個線程,為每一個可達表中的對象調用Finalize()方法。因此,為了真正終結一個對象,至少要進行兩次記憶體回收。
總而言之,儘管對象的終結能夠保證對象可以清除非託管的資源,但它本質上仍然是非確定的,而且由於額外的幕後處理,速度會變得相當慢。為了儘可能快地釋放非託管資源,我們引入了 IDisposable 介面,該介面定義了一個名為 Dispose() 的方法,該方法假設當對象的使用者不再使用該對象時,會這個對象引用離開範圍之前手工地調用 Dispose()。這樣對象可以執行任何必要的非託管資源的清除,而不會再有對象放在終結隊列上導致的效能損失,也不必等到記憶體回收觸發類的終結邏輯。
Finalize()只適用於類類型,而 IDisposable 介面對結構和類類型均適用。注意,如果對象支援 IDisposable 介面,那麼總是要對任何直接建立的對象調用 Dispose(),因為如果類設計者選擇實現該介面,說明該類需要手動執行清理工作。注意:基底類別庫中的許多類型實現了該介面,並提供了 Dispose() 方法的別名,如 System.IO.FileSteam類 Close()。我們可以不管別名,只調用 Dispose() 總是正確的。
3,可重用 using 關鍵字來簡化在 try/finally 塊中手動調用 Dispose() 方法的編寫。using 文法保證當使用支援 IDisposable介面的.NET類型的對象在退出using塊時,該對象會自動調用Dispose()方法。
4,可終結類型是重寫了System.Object.Finalize()虛方法(在解構函式中編寫清除代碼)以在回收垃圾時清除非託管資源的類。而可處置對象是實現了IDisposable介面的類或結構,對象使用者將在IDisposable介面被實行後調用它。我們可以將這兩種方式結合,如果對象使用者調用了Dispose(),則可以通過調用GC.SuppressFinalize()通知記憶體回收行程跳過終結過程;如果對象使用者忘記調用Dispose(),對象最終也將被終結並有機會釋放內部資源。對象的內部非託管資源總會用其中一種方式被釋放掉。