雖然在.net程式設計過程中,絕大多數記憶體垃圾**由clr(公共語言執行時)自動**,但也有很多需要我們編碼**。掌握託管與非託管的基本知識,可以有效避免某些情況下導致的程式異常。
託管**就是visual basic .net和c#編譯器編譯出來的**。編譯器把**編譯成中間語言(il),而不是能直接在你的電腦上執行的機器碼。中間語言被封裝在乙個叫程式集(assembly)的檔案中,程式集中包含了描述你所建立的類,方法和屬性(例如安全需求)的所有元資料。你可以拷貝這個程式集到另一台伺服器上部署它。通常來說,這個拷貝的動作就是部署流程中唯一的乙個操作。
託管**在公共語言執行庫(clr)中執行。這個執行庫給你的執行**提供各種各樣的服務,通常來說,他會載入和驗證程式集,以此來保證中間語言的正確性。當某些方法被呼叫的時候,執行庫把具體的方法編譯成適合本地計算機執行的機械碼,然後會把編譯好的機械碼快取起來,以備下次呼叫(這就是即時編譯)。隨著程式集的執行,執行庫會持續地提供各種服務,例如安全,記憶體管理,執行緒管理等等。這個程式被「託管」在執行庫中。visual basic .net和c#只能產生託管**。如果你用這類語言寫程式,那麼所產生的**就是託管**。
託管資源:一般是指被clr(公共語言執行時)控制的記憶體資源,這些資源由clr來管理。可以認為是.net 類庫中的資源。
非託管資源:不受clr控制和管理的資源。
對於託管資源,gc負責垃圾**。對於非託管資源,gc可以跟蹤非託管資源的生存期,但是不知道如何釋放它,這時候就要人工進行釋放。
非託管**就是在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。
原則:如果我們的類使用的非託管資源,如資料庫連線、檔案控制代碼,這些資源需做到:使用後立刻釋放。
選擇編譯器:為獲得公共語言執行庫提供的優點,必須使用乙個或多個針對執行庫的語言編譯器,如 visual basic、c#、visual c++、jscript 或許多第三方編譯器(如 eiffel、perl 或 cobol 編譯器)中的某乙個。由於執行庫是乙個多語言執行環境,因此它支援各種資料型別和語言功能。您所用的語言編譯器首先確定可用的執行庫功能,然後使用這些功能設計**。編譯器(而不是執行庫)建立**必須使用的語法。如果您的元件必須完全能夠被用其他語言編寫的元件使用,您的元件的匯出型別必須只公開公共語言規範 (cls) 中包括的語言功能。
編譯,將源**翻譯為microsoft中間語言(msil)並生成所需的元資料。
在執行時,實時 (jit) 編譯器將 msil 翻譯為本機**。在此編譯過程中,**必須通過驗證過程,該過程檢查 msil 和元資料以檢視是否可以將**確定為型別安全。
執行**:公共語言執行庫提供使執行能夠發生以及可在執行期間使用的各種服務的結構。
.net對於釋放資源,標準做法如下:
(1)繼承idisposable介面;
(2)實現dispose()方法,在其中釋放託管資源和非託管資源,並將物件本身從垃圾**器中移除(垃圾**器不在**此資源);
(3) 實現類析構函式,在其中釋放非託管資源。
對於dispose()方法的幾個引數說明:
a. 引數為true表示釋放所有資源,只能由使用者呼叫
引數為false表示釋放非託管資源,只能由垃圾**器自動呼叫
c. 如果子類有自己的非託管資源,可以過載這個函式,新增自己的非託管資源的釋放
d.但是要記住,過載此函式必須保證呼叫基類的版本,以保證基類的資源正常釋放
...有時間再寫..
(1)錯誤做法:
sqlconnection conn = newsqlconnection();
//do something;
conn.dispose();
此段**,如果出現異常conn.dispose()未能及時執行,會導致沒有及時關閉連線。
(2)正確做法:
sqlconnection conn = newsqlconnection();
tryfinally
對於以上**,我們還可以簡化**量,c#使用using簡化輸入,編譯器自動翻譯成 try...finally,改為下面寫法
using(sqlconnection conn = newsqlconnection())
a. 顯示呼叫dispose()方法,可以及時的釋放資源,同時通過移除finalize()方法的執行,提高了效能;
b. 如果沒有顯式呼叫dispose()方法,垃圾**器也可以通過析構函式來釋放非託管資源,垃圾**器本身就具有**託管資源的功能,從而保證資源的正常釋放,只不過由垃圾**器**會導致非託管資源的未及時釋放的浪費。
c. 在.net中應該盡可能的少用析構函式釋放資源。在沒有析構函式的物件在垃圾處理器一次處理中從記憶體刪除,但有析構函式的物件,需要兩次,第一次呼叫析構函式,第二次刪除物件。而且在析構函式中包含大量的釋放資源**,會降低垃圾**器的工作效率,影響效能。
d. 對於包含非託管資源的物件,最好及時的呼叫dispose()方法來**資源,而不是依賴垃圾**器
e. 析構函式只能由垃圾**器呼叫。
f. despose()方法只能由類的使用者呼叫。
g. 在.net中,凡是繼承了idisposable介面的類,都可以使用using語句,從而在超出作用域後,讓系統自動呼叫dispose()方法。
h. 乙個資源安全的類,都實現了idisposable介面和析構函式。提供手動釋放資源和系統自動釋放資源的雙保險。
(1)、finalize只釋放非託管資源;
(2)、dispose釋放託管和非託管資源;
(3)、重複呼叫finalize和dispose是沒有問題的;
(4)、finalize和dispose共享相同的資源釋放策略,因此他們之間也是沒有衝突的。
*************************=【origin and reference】*************************=
C 資源釋放
c 記憶體釋放,指標的存在,一直都是很困擾的乙個問題,怎樣釋放資源,成了很大的難題,本文拋磚引玉,整理自己對記憶體的一些理解,並記錄下來,不對之處,還望不吝指正。在c 中,記憶體分成5個區,他們分別是堆 棧 自由儲存區 全域性 靜態儲存區和常量儲存區。棧,在執行函式時,函式內區域性變數的儲存單元都可...
C 釋放資源
什麼是資源?net 框架中如何訪問資源?在物件導向的環境中,每乙個型別都標識為某些程式所用的資源,要想使用這些資源,必須為相應的型別分配一定的記憶體空間。訪問乙個資源需要如下幾個步驟 1 分配記憶體空間 呼叫中間語言 il 中的newobj指令 使用new操作符時,將產生newobj指令 為某個特定...
c 安全釋放資源
c 區域性函式中使用new分配記憶體或者create 建立com介面,由於程式可能在函式內下面的判斷 失敗退出,這樣每次都需要delete記憶體或者release,不僅麻煩而且可能忘記其中某個,造成資源沒有及時釋放.下面的通用的類可以解決這些問題 com版 template class t clas...