C#資源釋放

雖然在.NET編程過程當中,絕大多數內存垃圾回收由CLR(公共語言運行時)自動回收,但也有不少須要咱們編碼回收。掌握託管與非託管的基本知識,能夠有效避免某些狀況下致使的程序異常。html

1.託管與非託管

1.1什麼是託管與非託管?

託管代碼就是Visual Basic .NET和C#編譯器編譯出來的代碼。編譯器把代碼編譯成中間語言(IL),而不是能直接在你的電腦上運行的機器碼。中間語言被封裝在一個叫程序集(assembly)的文件中,程序集中包含了描述你所建立的類,方法和屬性(例如安全需求)的全部元數據。你能夠拷貝這個程序集到另外一臺服務器上部署它。一般來講,這個拷貝的動做就是部署流程中惟一的一個操做。數據庫

託管代碼在公共語言運行庫(CLR)中運行。這個運行庫給你的運行代碼提供各類各樣的服務,一般來講,他會加載和驗證程序集,以此來保證中間語言的正確性。當某些方法被調用的時候,運行庫把具體的方法編譯成適合本地計算機運行的機械碼,而後會把編譯好的機械碼緩存起來,以備下次調用(這就是即時編譯)。隨着程序集的運行,運行庫會持續地提供各類服務,例如安全,內存管理,線程管理等等。這個程序被「託管」在運行庫中。Visual Basic .NET和C#只能產生託管代碼。若是你用這類語言寫程序,那麼所產生的代碼就是託管代碼。編程

託管資源:通常是指被CLR(公共語言運行時)控制的內存資源,這些資源由CLR來管理。能夠認爲是.net 類庫中的資源。c#

非託管資源:不受CLR控制和管理的資源。緩存

對於託管資源,GC負責垃圾回收。對於非託管資源,GC能夠跟蹤非託管資源的生存期,可是不知道如何釋放它,這時候就要人工進行釋放。安全

1.2哪些資源是非託管的?

非託管代碼就是在Visual Studio .NET 2002發佈以前所建立的代碼,例如Visual Basic 6, Visual C++ 6。 最糟糕的是,連那些依然殘存在你的硬盤中、擁有超過15年曆史的陳舊C編譯器所產生的代碼都是非託管代碼。非託管代碼直接編譯成目標計算機的機械碼,這些代碼只能運行在編譯出它們的計算機上,或者是其它相同處理器或者幾乎同樣處理器的計算機上。非託管代碼不能享受一些運行庫所提供的服務,例如安全和內存管理等。若是非託管代碼須要進行內存管理等服務,就必須顯式地調用操做系統的接口,一般來講,它們會調用Windows SDK所提供的API來實現。就最近的狀況來看,非託管程序會經過COM接口來獲取操做系統服務。跟Visual Studio平臺的其餘編程語言不同,Visual C++能夠建立非託管程序。當你建立一個項目,而且選擇名字以MFC,ATL或者Win32開頭的項目類型,那麼這個項目所產生的就是非託管程序。服務器

總而言之,非託管代碼是運行在公共語言運行庫環境(CLR)的外部,由操做系統直接執行的代碼。非託管代碼必須提供本身的垃圾回收、類型檢查、安全支持等服務;它與託管代碼不一樣,後者從公共語言運行庫中得到這些服務。網絡

整體來講就是 不受CLR控制和管理的資源編程語言

包括:好比文件流、圖像圖形類、數據庫的鏈接,網絡鏈接,系統的窗口句柄,打印機資源等,這類資源通常不存在堆上。能夠認爲操做系統資源的一組API。函數

原則:若是咱們的類使用的非託管資源,如數據庫鏈接、文件句柄,這些資源需作到:使用後馬上釋放。

1.3託管代碼的執行過程

  1. 選擇編譯器:爲得到公共語言運行庫提供的優勢,必須使用一個或多個針對運行庫的語言編譯器,如 Visual Basic、C#、Visual C++、JScript 或許多第三方編譯器(如 Eiffel、Perl 或 COBOL 編譯器)中的某一個。因爲運行庫是一個多語言執行環境,所以它支持各類數據類型和語言功能。您所用的語言編譯器首先肯定可用的運行庫功能,而後使用這些功能設計代碼。編譯器(而不是運行庫)創建代碼必須使用的語法。若是您的組件必須徹底可以被用其餘語言編寫的組件使用,您的組件的導出類型必須只公開公共語言規範 (CLS) 中包括的語言功能。
  2. 編譯,將源代碼翻譯爲microsoft中間語言(MSIL)並生成所需的元數據。
  3. 在執行時,實時 (JIT) 編譯器將 MSIL 翻譯爲本機代碼。在此編譯過程當中,代碼必須經過驗證過程,該過程檢查 MSIL 和元數據以查看是否能夠將代碼肯定爲類型安全。
  4. 運行代碼:公共語言運行庫提供使執行可以發生以及可在執行期間使用的各類服務的結構。

2.如何釋放非託管資源?

.NET對於釋放資源,標準作法以下:

(1)繼承IDisposable接口;

(2)實現Dispose()方法,在其中釋放託管資源和非託管資源,並將對象自己從垃圾回收器中移除(垃圾回收器不在回收此資源);

(3) 實現類析構函數,在其中釋放非託管資源。

 對於Dispose()方法的幾個參數說明:

A. 參數爲true表示釋放全部資源,只能由使用者調用

  參數爲false表示釋放非託管資源,只能由垃圾回收器自動調用

C. 若是子類有本身的非託管資源,能夠重載這個函數,添加本身的非託管資源的釋放

D.可是要記住,重載此函數必須保證調用基類的版本,以保證基類的資源正常釋放 

2.1Dispose的實現方法

...有時間再寫..

2.2舉例說明資源釋放

2.2.1 數據庫鏈接釋放

(1)錯誤作法:  

SqlConnection conn = new SqlConnection();
//do something;
conn.Dispose();

此段代碼,若是出現異常conn.Dispose()未能及時執行,會致使沒有及時關閉鏈接。

(2)正確作法: 

SqlConnection conn = new SqlConnection();
try
{
    //do something;
}
finally
{
    conn.Dispose();
}

對於以上代碼,咱們還能夠簡化代碼量,c#使用using簡化輸入,編譯器自動翻譯成 try...finally,改成下面寫法

using(SqlConnection conn = new SqlConnection())
{
      //do something;
}

2.3對於釋放資源注意的幾點

A. 顯示調用Dispose()方法,能夠及時的釋放資源,同時經過移除Finalize()方法的執行,提升了性能;

B. 若是沒有顯式調用Dispose()方法,垃圾回收器也能夠經過析構函數來釋放非託管資源,垃圾回收器自己就具備回收託管資源的功能,從而保證資源的正常釋放,只不過由垃圾回收器回收會致使非託管資源的未及時釋放的浪費。

C. 在.NET中應該儘量的少用析構函數釋放資源。在沒有析構函數的對象在垃圾處理器一次處理中從內存刪除,但有析構函數的對象,須要兩次,第一次調用析構函數,第二次刪除對象。並且在析構函數中包含大量的釋放資源代碼,會下降垃圾回收器的工做效率,影響性能。

D. 對於包含非託管資源的對象,最好及時的調用Dispose()方法來回收資源,而不是依賴垃圾回收器

E. 析構函數只能由垃圾回收器調用。

F. Despose()方法只能由類的使用者調用。

G. 在.NET中,凡是繼承了IDisposable接口的類,均可以使用using語句,從而在超出做用域後,讓系統自動調用Dispose()方法。
H. 一個資源安全的類,都實現了IDisposable接口和析構函數。提供手動釋放資源和系統自動釋放資源的雙保險。

2.4 關於Finalize和Dispose

(1)、Finalize只釋放非託管資源;

(2)、Dispose釋放託管和非託管資源;

(3)、重複調用Finalize和Dispose是沒有問題的;

(4)、Finalize和Dispose共享相同的資源釋放策略,所以他們之間也是沒有衝突的。

 

==========================【Origin and Reference】==========================

https://www.cnblogs.com/yubinfeng/p/4625833.html

https://www.cnblogs.com/yangecnu/archive/2013/04/30/3052652.html

https://www.2cto.com/kf/201007/52838.html

相關文章
相關標籤/搜索