.NET 清理非托管資源
- Dispose
類型的 Dispose 方法應(yīng)釋放它擁有的所有資源。它還應(yīng)該通過調(diào)用其父類型的 Dispose 方法釋放其基類型擁有的所有資源。該父類型的 Dispose 方法應(yīng)該釋放它擁有的所有資源并同樣也調(diào)用其父類型的 Dispose 方法,從而在整個基類型層次結(jié)構(gòu)中傳播此模式。若要確保始終正確地清理資源,Dispose 方法應(yīng)該可以被多次調(diào)用而不引發(fā)任何異常。Dispose 方法應(yīng)該為它處置的對象調(diào)用 GC.SuppressFinalize 方法。如果對象當(dāng)前在終止隊列中,GC.SuppressFinalize 防止其 Finalize 方法被調(diào)用。請記住,執(zhí)行 Finalize 方法會大大減損性能。如果您的 Dispose 方法已經(jīng)完成了清理對象的工作,那么垃圾回收器就不必再調(diào)用對象的 Finalize 方法。
- 設(shè)計原則
應(yīng)用程序或類庫應(yīng)只允許一個線程擁有資源的生存期,并且應(yīng)在不再需要資源時調(diào)用 Dispose。根據(jù)資源的不同,在處置資源時進行異步線程訪問可能會帶來安全風(fēng)險。開發(fā)人員應(yīng)仔細檢查自己的代碼,以確定最佳的方法來強制線程安全。
- 代碼示例
- public class BaseResource: IDisposable
- {
- // 非托管資源 private IntPtr handle;
- // 托管資源 private Component Components;
- // 對象是否已被釋放的標(biāo)志 private bool disposed = false;
- public BaseResource() { }
- // 釋放資源,對外開放的可調(diào)用的方法
- public void Dispose()
- {
- // 釋放資源 Dispose(true);
- // 指示在析構(gòu)函數(shù)中跳過垃圾回收 GC.SuppressFinalize(this);
- }
- // 釋放資源,如果disposing為true,釋放所有的托管資源和非托管資源,如果為false,則僅僅釋放非托管資源,這主要是為了避免在析構(gòu)函數(shù)中重復(fù)2次進行垃圾回收
- protected virtual void Dispose(bool disposing)
- {
- // 檢查該對象是否已經(jīng)被釋放了
- if(!this.disposed)
- {
- if(disposing)
- {
- // 釋放托管資源 Components.Dispose();
- }
- // 釋放非托管資源 CloseHandle(handle);
- handle = IntPtr.Zero;
- }
- // 標(biāo)記該對象為已被釋放的對象 disposed = true;
- }
- // 析構(gòu)函數(shù),又名終結(jié)器
- ~BaseResource()
- {
- // 釋放非托管資源,在調(diào)用終結(jié)器方法時系統(tǒng)自動會對托管的資源進行垃圾回收
- Dispose(false);
- }
- // 允許多次調(diào)用Dispose,但會拋出異常publicvoid DoSomething()
- {
- if(this.disposed)
- {
- thrownew ObjectDisposedException();
- }
- }
- }
- 實現(xiàn) Close 方法
對于類型來說,若調(diào)用 Close 方法比調(diào)用 Dispose 方法更容易,則可以向基類型添加一個公共 Close 方法。Close 方法又會調(diào)用沒有參數(shù)的 Dispose 方法,該方法可以執(zhí)行正確的清理操作。在基礎(chǔ)類庫中的所有類的Close方法都是基于該原理構(gòu)造的。
- public void Close()
- {
- // 釋放資源
- Dispose();
- }
Finalize(終結(jié)器)
對于您的應(yīng)用程序創(chuàng)建的大多數(shù)對象,可以依靠 .NET Framework 的垃圾回收器隱式地執(zhí)行所有必要的內(nèi)存管理任務(wù)。但是,在您創(chuàng)建封裝非托管資源的對象時,當(dāng)您在應(yīng)用程序中使用完這些非托管資源之后,您必須顯式地釋放它們。
雖然垃圾回收器可以跟蹤封裝非托管資源的對象的生存期,但它不了解具體如何清理這些資源。對于這些類型的對象,.NET Framework 提供 Object.Finalize 方法,它允許對象在垃圾回收器回收該對象使用的內(nèi)存時適當(dāng)清理其非托管資源。但是對托管對象就不應(yīng)該實現(xiàn) Finalize方法,因為垃圾回收器會自動清理托管資源。
默認情況下,F(xiàn)inalize 方法不執(zhí)行任何操作。如果您要讓垃圾回收器在回收對象的內(nèi)存之前對對象執(zhí)行清理操作,您必須在類中重寫 Finalize 方法。但是在 C# 或 C++ 編程語言中無法重寫 Finalize 方法,所以在 C# 中可使用析構(gòu)函數(shù)語法實現(xiàn) Finalize 方法。
Finalize 方法主要是在未能調(diào)用 Dispose 方法的情況下充當(dāng)防護措施來清理資源。
實現(xiàn) Finalize 方法或析構(gòu)函數(shù)對性能可能會有負面影響,因此應(yīng)避免不必要地使用它們。
用 Finalize 方法回收對象使用的內(nèi)存需要至少兩次垃圾回收。當(dāng)垃圾回收器執(zhí)行回收時,它只回收沒有終結(jié)器的不可訪問對象的內(nèi)存。這時,它不能回收具有終結(jié)器的不可訪問對象。它改為將這些對象的項從終止隊列中移除并將它們放置在標(biāo)為準(zhǔn)備終止的對象列表中。該列表中的項指向托管堆中準(zhǔn)備被調(diào)用其終止代碼的對象。垃圾回收器為此列表中的對象調(diào)用 Finalize 方法,然后,將這些項從列表中移除。后來的垃圾回收將確定終止的對象確實是垃圾,因為標(biāo)為準(zhǔn)備終止對象的列表中的項不再指向它們。在后來的垃圾回收中,實際上回收了對象的內(nèi)存。
- 封裝資源對象
如果您要編寫代碼,而該代碼使用一個封裝資源的對象,您應(yīng)該確保在使用完該對象時調(diào)用該對象的 Dispose 方法。
- 封裝方式
- using語句
- try/finally塊