理解C#記憶體回收機制我們首先說一下CLR(通用語言執行平台,Common Language Runtime)它和Java虛擬機器一樣是一個運行時環境,核心功能包括:記憶體管理、程式集載入、安全性、非同步處理和線程同步。
CTS(Common Type System)一般型別系統,它把.Net中的類型分為2大類,參考型別與實值型別。.Net中所有類型都間接或直接派生至System.Object類型。所有的實值型別都是System.ValueType的子類,而System.ValueType本身卻是參考型別。
託管資源:
由CLR管理的存在於託管堆上的稱為託管資源,注意這裡有2個關鍵點,第一是由CLR管理,第二存在於託管堆上。託管資源的回收工作是不需要人工幹預的,CLR會在合適的時候調用GC(記憶體回收行程)進行回收。
非託管資源:
非託管資源是不由CLR管理,例如:Image Socket, StreamWriter, Timer, Tooltip, 檔案控制代碼, GDI資源, 資料庫連接等等資源(這裡僅僅列舉出幾個常用的)。這些資源GC是不會自動回收的,需要手動釋放。
通過上面的講述總結一下,第一,GC(記憶體回收行程)只回收託管資源,不回收非託管資源。第二,GC回收是要在合適的時候(CLR覺得應該進行回收的時候)才進行回收。那麼非託管如何進行回收呢?下面就讓我一一道來。
在.Net中釋放非託管資源主要有2種方式,Dispose,Finalize
Dispose方法,對象要繼承IDisposable介面,也就會自動調用Dispose方法。
Class Suifeng:System.IDisposable{ #region IDisposable 成員 public void Dispose() { // } #endregion}Suifeng suiFeng= new Suifeng ();suiFeng.Dispose();也可以使用Using語句(using Suifeng suiFeng= new Suifeng()){ //}
Finalize()方法
MSDN上的定義是允許對象在“記憶體回收”回收之前嘗試釋放資源並執行其他清理操作。
它的本質就是解構函式
class Car{ ~Car() // destructor { // cleanup statements... }}
該解構函式隱式地對對象的基類調用 Finalize。 這樣,前面的解構函式代碼被隱式地轉換為以下代碼:
protected override void Finalize(){ try { // Cleanup statements... } finally { base.Finalize(); }}
在.NET中應該儘可能的少用解構函式釋放資源,MSDN2上有這樣一段話:
實現 Finalize 方法或解構函式對效能可能會有負面影響,因此應避免不必要地使用它們。用 Finalize 方法回收對象使用的記憶體需要至少兩次記憶體回收。當記憶體回收行程執行回收時,它只回收沒有終結器的不可訪問對象的記憶體。這時,它不能回收具有終結器的不可訪問對象。它改為將這些對象的項從終止隊列中移除並將它們放置在標為準備終止的對象列表中。該列表中的項指向託管堆中準備被調用其終止代碼的對象。記憶體回收行程為此列表中的對象調用 Finalize 方法,然後,將這些項從列表中移除。後來的記憶體回收將確定終止的對象確實是垃圾,因為標為準備終止對象的列表中的項不再指向它們。在後來的記憶體回收中,實際上回收了對象的記憶體。
所以有解構函式的對象,需要兩次,第一次調用解構函式,第二次刪除對象。而且在解構函式中包含大量的釋放資原始碼,會降低記憶體回收行程的工作效率,影響效能。所以對於包含非託管資源的對象,最好及時的調用Dispose()方法來回收資源,而不是依賴記憶體回收行程。
在一個包含非託管資源的類中,關於資源釋放的標準做法是:
繼承IDisposable介面;
實現Dispose()方法,在其中釋放託管資源和非託管資源,並將對象本身從記憶體回收行程中移除(記憶體回收行程不在回收此資源);
實作類別解構函式,在其中釋放非託管資源。
請看MSDN上的源碼
Public class BaseResource:IDisposable { PrivateIntPtr handle; // 控制代碼,屬於非託管資源 PrivateComponet comp; // 組件,託管資源 Privateboo isDisposed = false; // 是否已釋放資源的標誌 PublicBaseResource { } //實現介面方法 //由類的使用者,在外部顯示調用,釋放類資源 Public void Dispose() { Dispose(true);// 釋放託管和非託管資源 //將對象從記憶體回收行程鏈表中移除, // 從而在記憶體回收行程工作時,只釋放託管資源,而不執行此對象的解構函式 GC.SuppressFinalize(this); } //由記憶體回收行程調用,釋放非託管資源 ~BaseResource() { Dispose(false);// 釋放非託管資源 } //參數為true表示釋放所有資源,只能由使用者調用 //參數為false表示釋放非託管資源,只能由記憶體回收行程自動調用 //如果子類有自己的非託管資源,可以重載這個函數,添加自己的非託管資源的釋放 //但是要記住,重載此函數必須保證調用基類的版本,以保證基類的資源正常釋放 Protectedvirtual void Dispose(bool disposing) { If(!this.disposed)// 如果資源未釋放 這個判斷主要用了防止對象被多次釋放 { If(disposing) { Comp.Dispose();// 釋放託管資源 } closeHandle(handle);// 釋放非託管資源 handle= IntPtr.Zero; } this.disposed= true; // 標識此對象已釋放 } }
參考了MSDN和網上的一些資料,第一次寫博文請各位大俠多多指點!