如果你經常看開源項目的源碼,你會發現很多Dispose方法中都有這麼一句代碼: ,看過一兩次可能無所謂,看多了就來了興趣,這篇就跟大家聊一聊。 一:背景 1. 在哪發現的 相信現在Mysql在.Net領域中鋪的面越來越廣了,C 對接MySql的MySql.Data類庫的代碼大家可以研究研究,幾乎所有 ...
如果你經常看開源項目的源碼,你會發現很多Dispose方法中都有這麼一句代碼: GC.SuppressFinalize(this);
,看過一兩次可能無所謂,看多了就來了興趣,這篇就跟大家聊一聊。
一:背景
1. 在哪發現的
相信現在Mysql在.Net領域中鋪的面越來越廣了,C#對接MySql的MySql.Data類庫的代碼大家可以研究研究,幾乎所有操作資料庫的幾大對象:MySqlConnection,MySqlCommand,MySqlDataReader以及內部的Driver都存在 GC.SuppressFinalize(this)
代碼。
public sealed class MySqlConnection : DbConnection, ICloneable
{
public new void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
public sealed class MySqlCommand : DbCommand, IDisposable, ICloneable
{
public new void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
2. GC.SuppressFinalize 場景在哪裡
先看一下官方對這個方法的解釋,如下所示:
//
// Summary:
// Requests that the common language runtime not call the finalizer for the specified
// object.
//
// Parameters:
// obj:
// The object whose finalizer must not be executed.
//
// Exceptions:
// T:System.ArgumentNullException:
// obj is null.
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SecuritySafeCritical]
public static void SuppressFinalize(object obj);
意思就是說: 請求 CLR 不要調用指定對象的終結器,如果你對終結器的前置基礎知識不足,那這句話肯定不是很明白,既然都執行了Dispose,說明非托管資源都被釋放了,怎麼還壓制CLR不要調用Finalize呢?刪掉和不刪掉這句代碼有沒有什麼嚴重的後果,GC類的方法誰也不敢動哈。。。 為了徹底講清楚,有必要說一下Finalize整個原理。
二:資源管理
我們都知道C#是一門托管語言,它的好處就是不需要程式員去關心記憶體的分配和釋放,由CLR統一管理,這樣編程門檻大大降低,天下攘攘皆為利來,速成系的程式員就越來越多~
1. 對托管資源和非托管資源理解
<1> 托管資源
這個很好理解,你在C#中使用的值類型,引用類型都是統一受CLR分配和GC清理。
<2> 非托管資源
在實際業務開發中,我們的代碼不可能不與外界資源打交道,比如說文件系統,外部網站,資料庫等等,就拿寫入文件的StreamWriter舉例,如下代碼:
public static void Main(string[] args)
{
StreamWriter sw = new StreamWriter("xxx.txt");
sw.WriteLine("....");
}
為什麼能夠寫入文件? 那是因為我們的代碼是請求windows底層的Win32 Api幫忙寫入的,這就有意思了,因為這個場景有第三者介入,sw是引用類型受CLR管理,win32 api屬於外部資源和.Net一點關係都沒有,如果你在用完sw之後沒有調用close方法的話,當某個時候GC回收了托管堆上的sw後,這給被打開的win32 api文件句柄再也沒有人可以釋放了,資源就泄露了,如果沒看懂,我畫張圖:
三:頭疼的非托管資源解決方案
1. 使用析構函數
很多時候程式員就是在使用完類之後因為種種原因忘記了手動執行Close方法造成了資源泄露,那有沒有一種機制可以在GC回收堆對象的時候回調我的一個自定義方法呢?如果能實現就