C#中的非托管资源释放(FinalizeDispose)
在了解Finalize和Dispose之前,我們需要了解兩個(gè)概念,一個(gè)是托管資源,一個(gè)非委托資源。
a.其中托管資源一般是指被CLR控制的內(nèi)存資源,這些資源的管理可以由CLR來(lái)控制,例如程序中分配的對(duì)象,作用域內(nèi)的變量等。
b.而非托管資源是CLR不能控制或者管理的部分,這些資源有很多,比如文件流,數(shù)據(jù)庫(kù)的連接,系統(tǒng)的窗口句柄,打印機(jī)資源等等……這些資源一般情況下不存在于Heap(內(nèi)存中用于存儲(chǔ)對(duì)象實(shí)例的地方)中。
.Net平臺(tái)中,CLR為程序員提供了一種很好的內(nèi)存管理機(jī)制,使得程序員在編寫(xiě)代碼時(shí)不需要顯式的去釋放自己使用的內(nèi)存資源(這些在先前C和C++中是需要程序員自己去顯式的釋放的)。這種管理機(jī)制稱為GC(garbage collection)。GC的作用是很明顯的,當(dāng)系統(tǒng)內(nèi)存資源匱乏時(shí),它就會(huì)被激發(fā),然后自動(dòng)的去釋放那些沒(méi)有被使用的托管資源(也就是程序員沒(méi)有顯式釋放的對(duì)象)。
但正如上面說(shuō)的,CLR的GC功能也只能釋放托管資源,對(duì)于非托管資源例如窗口,文件和網(wǎng)絡(luò)連接等,它都只能跟蹤非托管資源的生存期,而不知道如何去釋放它。這樣就會(huì)出現(xiàn)當(dāng)資源用盡時(shí)就不能提供資源能夠提供的服務(wù),windows的運(yùn)行速度就會(huì)變慢。這樣的情況會(huì)出現(xiàn)在數(shù)據(jù)庫(kù)的連接當(dāng)中,當(dāng)你沒(méi)有顯式的釋放一個(gè)數(shù)據(jù)庫(kù)資源時(shí),如果還是不斷的申請(qǐng)數(shù)據(jù)庫(kù)資源,那么到一定時(shí)候程序就會(huì)拋出一個(gè)異常。
所以,當(dāng)我們?cè)陬愔蟹庋b了對(duì)非托管資源的操作時(shí),我們就需要顯式,或者是隱式的釋放這些資源。而上面提到的Finalize和Dispose方法分別就是隱式和顯式操作中分別使用到的方法。
Finalize一般情況下用于基類不帶close方法或者不帶Dispose顯式方法的類,也就是說(shuō),在Finalize過(guò)程中我們需要隱式的去實(shí)現(xiàn)非托管資源的釋放,然后系統(tǒng)會(huì)在Finalize過(guò)程完成后,自己的去釋放托管資源。
如果要實(shí)現(xiàn)Dispose方法,可以通過(guò)實(shí)現(xiàn)IDisposable接口,這樣用戶在使用這個(gè)類的同時(shí)就可以顯示的執(zhí)行Dispose方法,釋放資源。
以下是MSDN上提出的Finalize和Dispose方法的使用指南,如果你的類遵循這個(gè)標(biāo)準(zhǔn)的話,你寫(xiě)出的類在.Net平臺(tái)上就是一個(gè)“良民”。
Finalize
下面的規(guī)則概括了 Finalize 方法的使用指南。
1.僅在要求終結(jié)的對(duì)象上實(shí)現(xiàn) Finalize。存在與 Finalize 方法相關(guān)的性能開(kāi)銷(xiāo)。
如果需要 Finalize 方法,應(yīng)考慮實(shí)現(xiàn) IDisposable,以使類的用戶可以避免調(diào)用 Finalize 方法帶來(lái)的開(kāi)銷(xiāo)。(juky_huang注:在實(shí)現(xiàn)IDisposable的類中,可以通過(guò)GC.SuppressFinalize來(lái)停止Finalize的運(yùn)行,這樣只要顯式的調(diào)用了Dispose方法,就能給用戶提供更小的開(kāi)銷(xiāo)。如果用戶沒(méi)有顯式的調(diào)用Dispose方法,也就是沒(méi)有停止Finalize的運(yùn)行,這樣就可以隱式的實(shí)現(xiàn)非托管資源的釋放)
2.不要使 Finalize 方法更可見(jiàn)。它應(yīng)該是 protected,而不是 public。 (juky_huang注:這個(gè)很重要,Finalize方法一般是系統(tǒng)調(diào)用,用戶不去顯式的調(diào)用它)
3.對(duì)象的 Finalize 方法應(yīng)該釋放對(duì)象擁有的任何外部資源。此外,Finalize 方法應(yīng)該僅釋放由對(duì)象控制的資源。Finalize 方法不應(yīng)該引用任何其他對(duì)象。
4.不要對(duì)不是對(duì)象的基類的對(duì)象直接調(diào)用 Finalize 方法。在 C# 編程語(yǔ)言中,這不是有效的操作。
5.從對(duì)象的 Finalize 方法調(diào)用 base.Finalize 方法。(juky_huang注:就是派生類調(diào)用基類的Finalize方法)
注意?? 基類的 Finalize 方法由 C# 和 C++ 的托管擴(kuò)展的析構(gòu)函數(shù)語(yǔ)法自動(dòng)調(diào)用。
Dispose
下面的規(guī)則概括了 Dispose 方法的使用指南:
1.在封裝明確需要釋放的資源的類型上實(shí)現(xiàn)處置設(shè)計(jì)方案。用戶可以通過(guò)調(diào)用公共 Dispose 方法釋放外部資源。
2.在通常包含控制資源的派生類型的基類型上實(shí)現(xiàn)處置設(shè)計(jì)方案,即使基類型并不需要。如果基類型有 close 方法,這通常指示需要實(shí)現(xiàn) Dispose。在這類情況下,不要在基類型上實(shí)現(xiàn) Finalize 方法。應(yīng)該在任何引入需要清理的資源的派生類型中實(shí)現(xiàn) Finalize。
3.使用類型的 Dispose 方法釋放類型所擁有的任何可處置資源。
4.對(duì)實(shí)例調(diào)用了 Dispose 后,禁止 Finalize 方法通過(guò)調(diào)用 GC.SuppressFinalize 方法運(yùn)行。此規(guī)則的例外情況是當(dāng)必須用 Finalize 完成 Dispose 沒(méi)有覆蓋的工作時(shí),但這種情況很少見(jiàn)。
5.如果基類實(shí)現(xiàn) IDisposable,則調(diào)用基類的 Dispose 方法。
6.不要假定 Dispose 將被調(diào)用。如果 Dispose 未被調(diào)用,也應(yīng)該使用 Finalize 方法釋放類型所擁有的非托管資源。
7.處置了資源之后,在該類型(非 Dispose)上從實(shí)例方法引發(fā)一個(gè) ObjectDisposedException。該規(guī)則不適用于 Dispose 方法,因?yàn)樵诓灰?/font>發(fā)異常的情況下,該方法應(yīng)該可以被多次調(diào)用。
8.通過(guò)基類型的層次結(jié)構(gòu)將調(diào)用傳播到 Dispose。Dispose 方法應(yīng)釋放此對(duì)象控制的所有資源和此對(duì)象所擁有的任何對(duì)象。例如,可以創(chuàng)建一個(gè)類似 TextReader 的對(duì)象來(lái)控制 Stream 和 Encoding,兩者均在用戶不知道的情況下由 TextReader 創(chuàng)建。另外,Stream 和 Encoding 都可以獲取外部資源。當(dāng)對(duì) TextReader 調(diào)用Dispose 方法時(shí),它應(yīng)該依次對(duì) Stream 和 Encoding 調(diào)用 Dispose,使它們釋放它們的外部資源。
9.應(yīng)考慮在調(diào)用了對(duì)象的 Dispose 方法后不允許使用對(duì)象。重新創(chuàng)建已處置的對(duì)象是難以實(shí)現(xiàn)的方案。
10.允許 Dispose 方法被調(diào)用多次而不引發(fā)異常。此方法在首次調(diào)用后應(yīng)該什么也不做。
有了以上的基礎(chǔ)后,我們看一段代碼,這段代碼是Dispose的一個(gè)實(shí)現(xiàn),這個(gè)代碼如果仔細(xì)的去考慮的話,非常的有趣,在這里我們又會(huì)看到C#中一個(gè)非常常用的技術(shù),多態(tài)性,如果你看過(guò)我在前面寫(xiě)的一篇關(guān)于虛擬方法的文章的話,你可以從中理解下面代碼的精要之處。
public class BaseResource: IDisposable
{
?// Pointer to an external unmanaged resource.
?// 非托管資源
?private IntPtr handle;
?// Other managed resource this class uses.
?// 托管資源
?private Component Components;
?// Track whether Dispose has been called.
?// 是否已經(jīng)釋放資源的標(biāo)志
?private bool disposed = false;
?// Constructor for the BaseResource object.
?public BaseResource()
?{
??// Insert appropriate constructor code here.
?}
?// Implement IDisposable.
?// Do not make this method virtual.
?// A derived class should not be able to override this method.
?// 提供給外部用戶顯示調(diào)用的方法,實(shí)際操作是在類的帶參數(shù)的虛函數(shù)Dispose(bool disposing)中實(shí)現(xiàn)
?public void Dispose()
?{
??// 表示用戶顯示調(diào)用
??Dispose(true);
??// Take yourself off the Finalization queue
??// to prevent finalization code for this object
??// from executing a second time.
??// 由于用戶是顯示調(diào)用,所以資源釋放不再由GC來(lái)完成
??GC.SuppressFinalize(this);
?}
?// Dispose(bool disposing) executes in two distinct scenarios.
?// If disposing equals true, the method has been called directly
?// or indirectly by a user's code. Managed and unmanaged resources
?// can be disposed.
?// If disposing equals false, the method has been called by the
?// runtime from inside the finalizer and you should not reference
?// other objects. Only unmanaged resources can be disposed.
?protected virtual void Dispose(bool disposing)
?{
??// Check to see if Dispose has already been called.
??// 如果已經(jīng)釋放,不做再次的操作,出現(xiàn)在用戶多次調(diào)用的情況下
??if(!this.disposed)
??{
???// If disposing equals true, dispose all managed
???// and unmanaged resources.
???if(disposing)
???{
????// Dispose managed resources.
????// 用戶是顯示調(diào)用的話,我們就要手工的操作托管資源
????Components.Dispose();
???}
???// Release unmanaged resources. If disposing is false,
???// only the following code is executed.
???CloseHandle(handle);
???handle = IntPtr.Zero;
???// Note that this is not thread safe.
???// Another thread could start disposing the object
???// after the managed resources are disposed,
???// but before the disposed flag is set to true.
???// If thread safety is necessary, it must be
???// implemented by the client.
??}
??disposed = true;????????
?}
?// Use C# destructor syntax for finalization code.
?// This destructor will run only if the Dispose method
?// does not get called.
?// It gives your base class the opportunity to finalize.
?// Do not provide destructors in types derived from this class.
?// 析構(gòu)函數(shù)
?~BaseResource()?????
?{
??// Do not re-create Dispose clean-up code here.
??// Calling Dispose(false) is optimal in terms of
??// readability and maintainability.
??// 表示本次調(diào)用是隱式調(diào)用,由Finalize方法調(diào)用,即托管資源釋放由GC來(lái)完成
??Dispose(false);
?}
?// Allow your Dispose method to be called multiple times,
?// but throw an exception if the object has been disposed.
?// Whenever you do something with this class,
?// check to see if it has been disposed.
?public void DoSomething()
?{
??if(this.disposed)
??{
???throw new ObjectDisposedException();
??}
?}
}
// Design pattern for a derived class.
// Note that this derived class inherently implements the
// IDisposable interface because it is implemented in the base class.
public class MyResourceWrapper: BaseResource
{
?// A managed resource that you add in this derived class.
?private ManagedResource addedManaged;
?// A native unmanaged resource that you add in this derived class.
?private NativeResource addedNative;
?private bool disposed = false;
?// Constructor for this object.
?public MyResourceWrapper()
?{
??// Insert appropriate constructor code here.
?}
??// 重寫(xiě)Dispose方法,釋放派生類自己的資源,并且調(diào)用基類的Dispose方法
?protected override void Dispose(bool disposing)
?{
??if(!this.disposed)
??{
???try
???{
????if(disposing)
????{
?????// Release the managed resources you added in
?????// this derived class here.
?????addedManaged.Dispose();????????
????}
????// Release the native unmanaged resources you added
????// in this derived class here.
????CloseHandle(addedNative);
????this.disposed = true;
???}
???finally
???{
????// Call Dispose on your base class.
????base.Dispose(disposing);
???}
??}
?}
}
// 在這里,派生類沒(méi)有實(shí)現(xiàn)~MyResourceWrapper和public Dispose方法,應(yīng)為他們已經(jīng)繼承了基類的這些特性,這也是我說(shuō)本示例代碼精要之處,他使用到了多態(tài)性原理,下面我會(huì)簡(jiǎn)單分析
// This derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits
// them from the base class.
本示例中有兩個(gè)類一個(gè)是基類BaseResource,一個(gè)是派生類MyResourceWrapper,首先我們必須理解一下幾點(diǎn):
1.類型的 Dispose 方法應(yīng)該釋放它擁有的所有資源。它還應(yīng)該通過(guò)調(diào)用其父類型的 Dispose 方法釋放其基類型擁有的所有資源。該父類型的Dispose 方法應(yīng)該釋放它擁有的所有資源并同樣也調(diào)用其父類型的 Dispose 方法,從而在整個(gè)基類型層次結(jié)構(gòu)中傳播該模式。
2.如果顯式的調(diào)用了Dispose方法,我們就在Dispose方法中實(shí)現(xiàn)托管資源和非托管資源的釋放,使用 GC.SuppressFinalize 方法來(lái)停止Finalize方法。因?yàn)槿绻脩粽{(diào)用了Dispose方法,那么我們就不必隱式的完成資源的釋放,應(yīng)為Finalizes會(huì)大大的減損性能。(Finalize一般只用于用戶沒(méi)有顯式的調(diào)用Dispose方法,需要我們隱式完成時(shí)才使用)
3.要確保始終正確地清理資源,Dispose 方法應(yīng)該可以被多次調(diào)用而不引發(fā)任何異常
帶參數(shù)的Dispose方法通過(guò)所帶的參數(shù)disposing來(lái)判斷,本次的Dispose操作是由Finalize發(fā)起還是由用戶顯式的調(diào)用公共Dispose方法發(fā)起的。如果為true則表示由公共的Dispose方法發(fā)起,如果為false表示是在GC調(diào)用Finalize方法時(shí)候發(fā)起。所以當(dāng)為true時(shí),我們就需要釋放托管資源和非托管資源,并且禁止GC的Finalize操作,因?yàn)橛脩艨梢灾苯油ㄟ^(guò)顯示調(diào)用來(lái)減小性能開(kāi)銷(xiāo)。如果為false時(shí),表示我們只需要釋放非托管資源,因?yàn)楸敬握{(diào)用是由GC的Finalize引起的,所以托管資源的釋放可以讓GC來(lái)完成。
示例中還有一個(gè)值得注意的地方,就是在多次顯示調(diào)用Dispose時(shí),如果資源已經(jīng)處置,那么我們就要忽略本次操作,而不拋出異常。這個(gè)特性由disposed來(lái)決定。
好了,現(xiàn)在我們來(lái)看看這個(gè)程序的一個(gè)精要之處,那就是在派生類中,沒(méi)有公共的Dispose方法,和Finalize方法(就是析構(gòu)函數(shù)),那如果我們調(diào)用派生類對(duì)象時(shí),是怎么實(shí)現(xiàn)資源釋放的呢,剛開(kāi)始我也不是很了解,后來(lái)仔細(xì)一看,突然發(fā)現(xiàn)其實(shí)很簡(jiǎn)單,它使用到了類的多態(tài)性來(lái)完成。
因?yàn)樵谂缮愔惺褂昧朔椒ㄖ貙?xiě),所以在派生類中的Dispose(bool disposing)方法的派生度最大。由于基類中的Finalize和公共Dispose方法都是調(diào)用的是Dispose(bool disposing)方法,所以最終調(diào)用的是派生度最大的哪個(gè)函數(shù),也就派生類中的Finalize和公共Dispose方法都是調(diào)用派生類自己的Dispose(bool disposing)方法。對(duì)于虛擬方法,可以參看我寫(xiě)的一篇文章地址是:
http://blog.csdn.net/juky_huang/archive/2005/10/26/517069.aspx
例如,現(xiàn)在我們有一個(gè)派生類實(shí)例A,如果我們顯示調(diào)用A.Dispose()方法,它會(huì)去調(diào)用基礎(chǔ)中的public Dispose方法這是由于繼承的原因,在public Dispose方法中調(diào)用的又是Dispose(bool disposing)方法,由于這個(gè)方法已經(jīng)被重寫(xiě),所以它實(shí)際調(diào)用的不是基類中的Dispose(bool disposing)方法,而是A自己的Dispose(bool disposing)方法。這是根據(jù)運(yùn)行時(shí)類型來(lái)定的。所以最終還是實(shí)現(xiàn)了,先調(diào)用A中的資源釋放,然后才調(diào)用base.Dispose方法來(lái)完成基類的資源釋放。
如果用戶沒(méi)有顯示調(diào)用Dispose方法,那么Finalize方法就會(huì)有效,過(guò)程和上面是類似的。
從上面可以看出,對(duì)于非托管資源的釋放,有一個(gè)很好的規(guī)則,只要我們按照這個(gè)規(guī)則來(lái)做,你寫(xiě)的代碼就是.Net中的“良民”。
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的C#中的非托管资源释放(FinalizeDispose)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Silverlight的4个版本
- 下一篇: C#强化系列文章三:实验分析C#中三种计