C 託管程式中的資源釋放問題

2022-02-27 03:21:18 字數 3584 閱讀 4124

第乙個就是很多人用.net寫程式,會談到託管這個概念。那麼.net所指的資源託管到底是什麼意思,是相對於所有資源,還是只限於某一方面資源?很多人對此不是很了解,其實

.net

所指的託管只是針對記憶體這乙個方面,並不是對於所有的資源;因此對於

stream

,資料庫的連線,

gdi+

的相關物件,還有

com物件等等,這些資源並不是受到

.net

管理而統稱為非託管資源。而對於記憶體的釋放和**,系統提供了

gc-garbage collector

,而至於其他資源則需要手動進行釋放。

那麼第二個概念就是什麼是垃圾,通過我以前的文章,會了解到.net型別分為兩大類,乙個就是值型別,另乙個就是引用型別。前者是分配在棧上,並不需要gc**;後者是分配在堆上,因此它的記憶體釋放和**需要通過gc來完成。gc的全稱為「garbage collector」,顧名思義就是垃圾**器,那麼只有被稱為垃圾的物件才能被gc**。也就是說,乙個引用型別物件所占用的記憶體需要被

gc**,需要先成為垃圾。那麼

.net

如何判定乙個引用型別物件是垃圾呢,

.net

的判斷很簡單,只要判定此物件或者其包含的子物件沒有任何引用是有效的,那麼系統就認為它是垃圾。

明確了這兩個基本概念,接下來說說gc的運作方式以及其的功能。記憶體的釋放和**需要伴隨著程式的執行,因此系統為gc安排了獨立的執行緒。那麼gc的工作大致是,查詢記憶體中物件是否成為垃圾,然後對垃圾進行釋放和**。那麼對於gc對於記憶體**採取了一定的優先演算法進行輪循**記憶體資源。其次,對於記憶體中的垃圾分為兩種,一種是需要呼叫物件的析構函式,另一種是不需要呼叫的。

gc對於前者的**需要通過兩步完成,第一步是呼叫物件的析構函式,第二步是**記憶體,但是要注意這兩步不是在

gc一次輪循完成,即需要兩次輪循;相對於後者,則只是**記憶體而已。

很明顯得知,對於某個具體的資源,無法確切知道,物件析構函式什麼時候被呼叫,以及gc什麼時候會去釋放和**它所占用的記憶體。那麼對於從c、c++之類語言轉換過來的程式設計師來說,這裡需要轉變觀念。

那麼對於程式資源來說,我們應該做些什麼,以及如何去做,才能使程式效率最高,同時占用資源能盡快的釋放。前面也說了,資源分為兩種,託管的記憶體資源,這是不需要我們操心的,系統已經為我們進行管理了;那麼對於非託管的資源,這裡再重申一下,就是stream,資料庫的連線,gdi+的相關物件,還有com物件等等這些資源,需要我們手動去釋放。

如何去釋放,應該把這些操作放到**比較好呢。.net提供了三種方法,也是最常見的三種,大致如下:

1.析構函式;

2.繼承idisposable介面,實現dispose方法;

3.提供close方法。

經過前面的介紹,可以知道析構函式只能被

gc來呼叫的,那麼無法確定它什麼時候被呼叫,因此用它作為資源的釋放並不是很合理,因為資源釋放不及時;但是為了防止資源洩漏,畢竟它會被

gc呼叫,因此析構函式可以作為乙個補救方法。而

close

與dispose

這兩種方法的區別在於,呼叫完了物件的

close

方法後,此物件有可能被重新進行使用;而

dispose

方法來說,此物件所占有的資源需要被標記為無用了,也就是此物件被銷毀了,不能再被使用。

例如,常見sqlconnection這個類,當呼叫完close方法後,可以通過open重新開啟資料庫連線,當徹底不用這個物件了就可以呼叫dispose方法來標記此物件無用,等待gc**。明白了這兩種方法的意思後,大家在往自己的類中新增的介面時候,不要歪曲了這兩者意思。

接下來說說這三個函式的呼叫時機,我用幾個試驗結果來進行說明,可能會使大家的印象更深。

首先是這三種方法的實現,大致如下:

//////

the class to show three disposal function

///public

classdisposeclass:idisposable

~disposeclass()

#region

idisposable members

public

voiddispose()

#endregion

}對於close來說不屬於真正意義上的釋放,除了注意它需要顯示被呼叫外,我在此對它不多說了。而對於析構函式而言,不是在物件離開作用域後立刻被執行,只有在關閉程序或者呼叫gc.collect方法的時候才被呼叫,參看如下的**執行結果。

private

voidcreate()

private

voidcallgc()

// show destructor

create();

debug.writeline( "after created!" );

callgc();

執行的結果為:

after created!

destructor called!

顯然在出了

create

函式外,

myclass

物件的析構函式沒有被立刻呼叫,而是等顯示呼叫gc.collect才被呼叫。

對於dispose來說,也需要顯示的呼叫,但是對於繼承了idisposable的型別物件可以使用using這個關鍵字,這樣物件的dispose方法在出了using範圍後會被自動呼叫。例如:

using( disposeclass myclass =newdisposeclass() )

如上執行的結果如下:

dispose called!

那麼對於如上

disposeclass

型別的dispose實現來說,事實上gc還需要呼叫物件的析構函式,按照前面的

gc流程來說,

gc對於需要呼叫析構函式的物件來說,至少經過兩個步驟,即首先呼叫物件的析構函式,其次**記憶體。也就是說,按照上面所寫的

dispose

函式,雖說被執行了,但是

gc還是需要執行析構函式,那麼乙個完整的

dispose

函式,應該通過呼叫

gc.suppressfinalize(this )

來告訴gc

,讓它不用再呼叫物件的析構函式中。

那麼改寫後的

disposeclass

如下:///

///the class to show three disposal function

///public

classdisposeclass:idisposable

~disposeclass()

#region

idisposable members

public

voiddispose()

#endregion

}通過如下的**進行測試。

private

voidrun()

}private

voidcallgc()

// show destructor

run();

debug.writeline( "after run!" );

callgc();

執行的結果如下:

dispose called!

after run!

C 關於託管程式和非託管程式的區別

一般的乙個可執行檔案的內容都包含乙個pe頭,系統根據pe的資訊找到入口函式,通過執行入口函式中的 來執行可執行程式。託管程式的檔案相對於非託管程式還包含了乙個clr表頭檔案以及其他clr需要的資訊。1.非託管程式的執行過程 在非託管程式中,可執行裡面儲存的是機器 cpu可以直接載入並執行,當系統載入...

C 釋放非託管資源

c 中資源分為託管資源和非託管資源。託管資源由垃圾 器控制如何釋放,不需要程式設計師過多的考慮 當然也程式設計師也可以自己釋放 非託管資源需要自己編寫 來釋放。那麼編寫好的釋放非託管資源的 釋非 由誰來呼叫呢。有兩種實現方式 一 將釋非 放到建構函式析構函式中,由系統自動呼叫,系統會在資源物件不再使...

非託管資源的釋放

與c 一樣,c 可以定義析構函式。但c 的析構函式主要用於釋放託管資源。在net中,由gc垃圾 執行緒掌握物件資源的釋放,程式設計師無法掌控析構函式的呼叫時機。為了完全掌控非託管資源的釋放,net提供乙個idisposable介面。問題 finalize 和dispose 之間的區別?最簡單的解釋是...