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