C#中的數(shù)據(jù)類型
C#中的所有類型都是(直接或間接)從System.Object類型派生的愤诱。
C#的類型被分成兩大類——引用類型(reference type)超营,分配在內(nèi)存堆上;值類型(value type),分配在堆棧上。如圖:
值類型在棧里,先進后出冠跷,值類型變量的生命有先后順序,這個確保了值類型變量在退出作用域以前會釋放資源身诺。堆棧是從高地址往低地址分配內(nèi)存蜜托。
引用類型分配在托管堆(Managed Heap)上,聲明一個變量在棧上保存霉赡,當(dāng)使用new創(chuàng)建對象時橄务,會把對象的地址存儲在這個變量里。托管堆相反穴亏,從低地址往高地址分配內(nèi)存仪糖,如圖:
托管資源
托管資源指的是.NET可以自動進行回收的資源,被CLR控制的內(nèi)存資源迫肖,這些資源的管理可以由CLR來控制锅劝,主要是指托管堆上分配的內(nèi)存資源。
托管資源的回收工作是不需要人工干預(yù)的蟆湖,由.NET運行庫在合適調(diào)用垃圾回收器進行回收故爵。
托管資源:從文字上看就是托付給別人管理,就像.NET的CLR隅津,java的jvm诬垂。
.NET中超過80%的資源都是托管資源。
非托管資源
非托管資源指的是.NET不知道如何回收的資源伦仍,是CLR不能控制或者管理的部分结窘,最常見的一類非托管資源是包裝操作系統(tǒng)資源的對象,例如文件
充蓝,窗口
隧枫,網(wǎng)絡(luò)連接
,數(shù)據(jù)庫連接
谓苟,畫刷
官脓,圖標
等。
ApplicationContext, Brush, Component, ComponentDesigner, Container, Context, Cursor, FileStream, Font, Icon, Image, Matrix, Object, OdbcDataReader, OleDBDataReader, Pen, Regex, Socket, StreamWriter, Timer, Tooltip, 文件句柄, GDI資源, 數(shù)據(jù)庫連接等等資源涝焙,這些都是非托管資源
這些資源一般情況下不存在于托管堆中卑笨。垃圾回收器在清理的時候會調(diào)用Object.Finalize()方法。默認情況下仑撞,方法是空的赤兴,對于非托管對象妖滔,需要在此方法中編寫回收非托管資源的代碼,以便垃圾回收器正確回收資源桶良。但在.NET中座舍,Object.Finalize()方法是無法重載的,編譯器是根據(jù)類的析構(gòu)函數(shù)來自動生成Object.Finalize()方法的艺普,所以對于包含非托管資源的類簸州,可以將釋放非托管資源的代碼放在析構(gòu)函數(shù)鉴竭。
?? 不能在析構(gòu)函數(shù)中釋放托管資源歧譬,因為析構(gòu)函數(shù)是有垃圾回收器調(diào)用的,可能在析構(gòu)函數(shù)調(diào)用之前搏存,類包含的托管資源已經(jīng)被回收了瑰步,從而導(dǎo)致無法預(yù)知的結(jié)果。
IDisposable接口
如果按照上面做法璧眠,非托管資源也能夠由垃圾回收器進行回收缩焦,但是非托管資源一般是有限的,比較寶貴的责静,而垃圾回收器是由CRL自動調(diào)用的袁滥,這樣就無法保證及時的釋放掉非托管資源,因此定義了一個Dispose()方法灾螃,讓使用者能夠手動的釋放非托管資源题翻。
- Dispose()方法釋放類的托管資源和非托管資源,使用者手動調(diào)用此方法后腰鬼,垃圾回收器不會對此類實例再次進行回收嵌赠。
- Dispose()方法是由使用者調(diào)用的,在調(diào)用時熄赡,類的托管資源和非托管資源肯定都未被回收姜挺,所以可以同時回收兩種資源。
- 如果我們不想為一個類型實現(xiàn)Dispose方法彼硫,但我們想讓它自動的釋放非托管資源炊豪。那么就如上文所說,將釋放非托管資源的代碼放在析構(gòu)函數(shù)拧篮。
Microsoft為非托管資源的回收專門定義了一個接口:IDisposable溜在,接口中只包含一個Dispose()方法。任何包含非托管資源的類他托,都應(yīng)該繼承此接口掖肋。
在一個包含非托管資源的類中,關(guān)于資源釋放的標準做法是:
1)繼承IDisposable接口赏参;
2)實現(xiàn)Dispose()方法志笼,在其中釋放托管資源和非托管資源沿盅,并將對象本身從垃圾回收器中移除(垃圾回收器不在回收此資源);
3)實現(xiàn)類析構(gòu)函數(shù)纫溃,在其中釋放非托管資源腰涧。
只要按照上面要求的步驟編寫代碼,該類就屬于資源安全的類紊浩。
解釋:在使用時窖铡,顯示調(diào)用Dispose()方法,可以及時的釋放資源坊谁,同時通過移除Finalize()方法的執(zhí)行费彼,提高了性能;如果沒有顯示調(diào)用Dispose()方法口芍,垃圾回收器也可以通過析構(gòu)函數(shù)來釋放非托管資源箍铲,垃圾回收器本身就具有回收托管資源的功能,從而保證資源的正常釋放鬓椭,只不過由垃圾回收器回收會導(dǎo)致非托管資源的未及時釋放的浪費颠猴。
??在.NET中應(yīng)該盡可能的少用析構(gòu)函數(shù)釋放資源。在沒有析構(gòu)函數(shù)的對象在垃圾處理器一次處理中從內(nèi)存刪除小染,但有析構(gòu)函數(shù)的對象翘瓮,需要兩次,第一次調(diào)用析構(gòu)函數(shù)裤翩,第二次刪除對象资盅。而且在析構(gòu)函數(shù)中包含大量的釋放資源代碼,會降低垃圾回收器的工作效率岛都,影響性能律姨。所以對于包含非托管資源的對象,最好及時的調(diào)用Dispose()方法來回收資源臼疫,而不是依賴垃圾回收器择份。
附上MSDN的代碼,大家可以參考烫堤。
public class BaseResource : IDisposable
{
// 指向外部非托管資源
private IntPtr handle;
// 此類使用的其它托管資源.
private Component Components;
// 跟蹤是否調(diào)用.Dispose方法荣赶,標識位,控制垃圾收集器的行為
private bool disposed = false;
// 構(gòu)造函數(shù)
public BaseResource()
{
// Insert appropriate constructor code here.
}
// 實現(xiàn)接口IDisposable.
// 不能聲明為虛方法virtual.
// 子類不能重寫這個方法.
public void Dispose()
{
Dispose(true);
// 離開終結(jié)隊列Finalization queue
// 設(shè)置對象的阻止終結(jié)器代碼
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) 執(zhí)行分兩種不同的情況.
// 如果disposing 等于 true, 方法已經(jīng)被調(diào)用
// 或者間接被用戶代碼調(diào)用. 托管和非托管的代碼都能被釋放
// 如果disposing 等于false, 方法已經(jīng)被終結(jié)器 finalizer 從內(nèi)部調(diào)用過鸽斟,
// 你就不能在引用其他對象拔创,只有非托管資源可以被釋放。
protected virtual void Dispose(bool disposing)
{
// 檢查Dispose 是否被調(diào)用過.
if (!this.disposed)
{
// 如果等于true, 釋放所有托管和非托管資源
if (disposing)
{
// 釋放托管資源.
Components.Dispose();
}
// 釋放非托管資源富蓄,如果disposing為 false,
// 只會執(zhí)行下面的代碼.
CloseHandle(handle);
handle = IntPtr.Zero;
// 注意這里是非線程安全的.
// 在托管資源釋放以后可以啟動其它線程銷毀對象剩燥,
// 但是在disposed標記設(shè)置為true前
// 如果線程安全是必須的,客戶端必須實現(xiàn)。
}
disposed = true;
}
// 使用interop 調(diào)用方法
// 清除非托管資源.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
// 使用C# 析構(gòu)函數(shù)來實現(xiàn)終結(jié)器代碼
// 這個只在Dispose方法沒被調(diào)用的前提下灭红,才能調(diào)用執(zhí)行侣滩。
// 如果你給基類終結(jié)的機會.
// 不要給子類提供析構(gòu)函數(shù).
~BaseResource()
{
// 不要重復(fù)創(chuàng)建清理的代碼.
// 基于可靠性和可維護性考慮,調(diào)用Dispose(false) 是最佳的方式
Dispose(false);
}
// 允許你多次調(diào)用Dispose方法,
// 但是會拋出異常如果對象已經(jīng)釋放变擒。
// 不論你什么時間處理對象都會核查對象的是否釋放,
// check to see if it has been disposed.
public void DoSomething()
{
if (this.disposed)
{
thrownew ObjectDisposedException();
}
}
// 不要設(shè)置方法為virtual.
// 繼承類不允許重寫這個方法
public void Close()
{
// 無參數(shù)調(diào)用Dispose參數(shù).
Dispose();
}
public static void Main()
{
// Insert code here to create
// and use a BaseResource object.
}
}
.NET Framework的System.GC類提供了控制Finalize的兩個方法君珠,ReRegisterForFinalize和SuppressFinalize。前者是請求系統(tǒng)完成對象的Finalize方法娇斑,后者是請求系統(tǒng)不要完成對象的Finalize方法策添。當(dāng)你用Dispose方法釋放未托管對象的時候,應(yīng)該調(diào)用GC.SuppressFinalize毫缆。GC.SuppressFinalize會阻止GC調(diào)用Finalize方法唯竹。因為Finalize方法的調(diào)用會犧牲部分性能。如果你的Dispose方法已經(jīng)對委托管資源作了清理悔醋,就沒必要讓GC再調(diào)用對象的Finalize方法(MSDN)摩窃。
析構(gòu)函數(shù)只能由垃圾回收器調(diào)用兽叮。
Despose()方法只能由類的使用者調(diào)用芬骄。
在C#中,凡是繼承了IDisposable接口的類鹦聪,都可以使用using語句账阻,從而在超出作用域后,讓系統(tǒng)自動調(diào)用Dispose()方法泽本。 一個資源安全的類淘太,都實現(xiàn)了IDisposable接口和析構(gòu)函數(shù)。提供手動釋放資源和系統(tǒng)自動釋放資源的雙保險规丽。