網站首頁 編程語言 正文
一、前言
在C#中,由于有了垃圾回收機制的支持,對象的析構和以前的C++有了很大的不同,這就要求程序員在設計類型的時候,充分理解.NET的機制,明確怎樣利用Dispose方法和Finalize方法來保證一個對象正確而高效地被析構。
二、Dispose方法的功能
我們在講解有關using的用法時,已經介紹了Dispose方法。正是因為垃圾回收機制掩蓋了對象內存真正被回收的時間,考慮到很多情況下程序員扔希望在對象不再被使用的時候進行一些清理工作,所以.NET提供了IDisposable接口并且在其中定義了Dispose方法。通常程序員會在Dispose方法中實現(xiàn)一些托管對象和非托管對象的釋放以及邏輯業(yè)務的結束工作等。注意實現(xiàn)了Dispose方法不能得到任何有關釋放的保證,Dispose方法的調用依賴于類型的使用者,當類型被不恰當?shù)厥褂脮r,Dispose方法將不會被調用,但using等語法的存在還是幫助了類型的Dispose方法被調用。
三、Finalize方法的機制
由于Dispose方法的調用依賴于使用者,為了彌補這一缺陷,.NET同時提供了Finalize方法。Finalize方法常常被具有C++開發(fā)經驗的程序員稱為析構方法,但它的執(zhí)行方法卻和傳統(tǒng)C++中的析構函數(shù)完全不同。Finalize方法在GC執(zhí)行垃圾回收時調用,具體的機制是這樣的:
- 當每個包含F(xiàn)inalize方法的類型的實例對象被分配時,.NET會在一張?zhí)囟ǖ谋斫Y構中添加一個引用并且執(zhí)行這個實例對象。方便起見稱該表為“帶析構對象表”。
- 當GC執(zhí)行并且檢測到一個不被使用的對象時,需要進一步檢查“帶析構對象表”來查看該對象類型是否有Finalize方法,如果沒有則該對象被視為垃圾,如果存在Finalize方法,則把指向該對象的引用從“帶析構對象表”移到另外一張表中,這里暫時稱它為“等待析構表”。并且該對象實例被視為扔在被使用。
- CLR將有一個單獨的線程負責處理“等待析構表”,其方法就是依次通過引用調用其中每個對象的Finalize方法,然后刪除引用,這時托管堆中的對象實例將處于不再被使用的狀態(tài)。
- 下一個GC執(zhí)行時,將釋放已經被調用Finalize方法的那些對象實例。
四、正確地使用Dispose和Finalize方法
Finalize方法確實比Dispose方法更加安全,因為它由CLR保證調用,但是性能方面Finalize方法卻要差的多。我們需要知道的是:正確的類型設計是把Finalize方法作為Dispose方法的后備,只有在使用者沒有調用Dispose方法的情況下,F(xiàn)inalize方法才能被視為需要執(zhí)行。下面是一個正確高效的設計模板,建議牢記這個模板并且套用到每一個需要DIspose和Finalize方法的類型上去。
using System; namespace usingDemo { public class FinalizeDisposeBase : IDisposable { // 標記對象是否已被釋放 private bool _disposed = false; // Finalize方法 ~FinalizeDisposeBase() { Dispose(false); } ////// 這里實現(xiàn)了IDisposable中的Dispose方法 /// public void Dispose() { Dispose(true); // 告訴GC此對象的Finalize方法不再需要調用 GC.SuppressFinalize(true); } ////// 在這里做實際的析構工作 /// 聲明為虛方法以供子類在必要時重寫 /// /// protected virtual void Dispose(bool isDisposing) { // 當對象已經被析構時,不在執(zhí)行 if(_disposed) { return; } if(isDisposing) { // 在這里釋放托管資源 // 只在用戶調用Dispose方法時執(zhí)行 } // 在這里釋放非托管資源 // 標記對象已被釋放 _disposed = true; } } public sealed class FinalizeDispose:FinalizeDisposeBase { private bool _mydisposed = false; protected override void Dispose(bool isDisposing) { // 保證只釋放一次 if (_mydisposed) { return; } if(isDisposing) { // 在這里釋放托管的并且在這個類型中聲明的資源 } // 在這里釋放非托管的并且在這個類型中聲明的資源 // 調用父類的Dispose方法來釋放父類中的資源 base.Dispose(isDisposing); // 設置子類的標記 _mydisposed = true; } static void Main() { } } }
上面的代碼是一個近乎完美的Dispose配合Finalize的設計模板,其中有幾點需要特別注意:
- 真正做釋放工作的只是Virtual的受保護方法Dispose方法,事實上這個方法的名字并不重要,僅僅為了通用和更好理解,稱呼它為Dispose。
- 虛方法Dispose需要接受一個布爾類型的參數(shù),主要用于區(qū)分調用方是類型的使用者還是.NET的垃圾回收。前者通過IDisposable的Dispose方法,而后者通過Finalize方法。兩者的區(qū)別是通過Finalize方法釋放資源時不能再釋放或使用對象中的托管資源,這是因為這時的對象已經處于不被使用的狀態(tài),很有可能其中的托管資源已經被釋放掉了。
- 在IDisposable的Dispose方法的實現(xiàn)中通過GC.SuppressFinalize()方法來告訴.NET此對象在被回收時不需要調用Finalize方法,這一句是改善性能的關鍵,記住實現(xiàn)Dispose方法的本質目的就是避免所有釋放工作在Finalize方法中進行。
- 子類型必須定義自己的釋放標記來標明子類中的資源是否已經被釋放,同時子類的虛方法Dispose方法也只需要釋放自己新定義的資源。
- 確保在虛方法Dispose中做的都是釋放工作,有些邏輯上的結束工作需要反復斟酌,以防止一個簡單的賦值語句使對象再度存活。
五、總結
Dispose方法被使用者主動調用,而Finalize方法在對象被垃圾回收的第一輪回收后,由一個專用的.NET線程進行調用。Dispose方法不能保證被執(zhí)行,而.NET的垃圾回收機制保證了擁有Finalize方法并且需要被調用的類型對象的Finalize方法被執(zhí)行。調用Finalize方法涉及了一系列復雜的操作,性能代價非常高,程序員可以通過GC.SuppressFinalize方法通知.NET該對象的Finalize方法不需要被調用。有關Dispose和Finalize的類型設計應該參照上面的代碼模板,以保證對象能夠被高效和安全的釋放。
原文鏈接:https://www.cnblogs.com/dotnet261010/p/12330981.html
相關推薦
- 2022-07-25 C/C++實現(xiàn)線性順序表的示例代碼_C 語言
- 2022-05-06 excelize-golang中excel表格內容讀取
- 2022-12-13 Android?DataBinding單向數(shù)據(jù)綁定深入探究_Android
- 2022-11-04 Linux下自動刪除過期備份和自動異地備份的腳本_linux shell
- 2022-05-09 輕量級ORM框架Dapper應用之實現(xiàn)DTO_實用技巧
- 2022-04-21 Mybatis - tk.mybatis deleteByPrimaryKey無法正確識別主鍵
- 2023-06-18 c#關于非托管內存的釋放問題及解讀_C#教程
- 2022-09-18 iOS開發(fā)探索多線程GCD隊列示例詳解_IOS
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支