C# 實值型別和參考型別的記憶體管理

來源:互聯網
上載者:User
本次日誌,我們來重點聊一聊軟體開發過程中,如何提高效能方面的問題。這是軟體開發或研發過程中深層次的問題,這篇文章主要從記憶體配置和記憶體回收兩方面說明,我們軟體代碼編寫過程中,計算如何來工作的。在此你可以瞭解記憶體管理的過程和方式,以便在以後的軟體開發中注意它、利用它。

實值型別包括:int,float,double,bool,結構,引用,表示對象執行個體的變數

參考型別包括:類和數組;比較特殊的參考型別string、object

一般情況下:實值型別儲存在堆棧中(不包括包含在引用中的實值型別,如類的值欄位,類中的引用欄位,數組的元素這些都是隨引用儲存在受管制的堆中);參考型別儲存在受管制的堆中,為什麼說是受管制的堆,下面會具體來談。

幾個概念:

虛擬記憶體:32位的電腦,每個進程擁有4G的虛擬記憶體。

受管制的堆(託管堆):受誰的管制?當然是無用單元收集器,即垃圾收集器。如何管理下面再說?

無用單元收集器:垃圾收集器除了會壓縮託管堆、更新引用地址、還會維護託管堆的資訊列表等等。

關於實值型別的儲存先看如下代碼:

{

int age=20;

double salary=2000;

}

上面定義的兩個變數,int age,告訴編譯器需要給我分配4個位元組的記憶體空間來儲存age值,它是儲存在堆棧上的。堆棧屬入先進後出的資料結構,堆棧是從高位地址到低位地址儲存資料的。電腦寄存器中保持著一個堆棧指標,他總是指向堆棧最底端的自由空間地址,當我們定義一個int類型的值時,堆棧指標遞減四個位元組的地址;當變數出了範圍後,堆棧指標相應的相應的遞增四個位元組的地址,它只是堆棧指標的上下移動,所以堆棧的效能是相當高的。

下面看一下參考型別的儲存,還是先看代碼:

{

Customer customerA;

customerA=new Customer(); //假定Customer執行個體佔據32個位元組

}

上面的代碼第一行先聲明了一個Customer引用,引用的名字為customerA,在堆棧上給此引用分配儲存空間,儲存空間的大小為4個位元組,因為它只儲存了一個引用,這個引用所指向的才是即將儲存Customer執行個體的空間地址,注意此時customerA並沒有指向具體的空間,它只是分配了一個空間而已。

第二行執行過程中,.net環境會搜尋託管堆,尋找第一個未使用的、連續的32個位元組空間分配給類的執行個體,並設定customerA指向這段空間的頂端位置(堆的空間是從低到高使用的)。當引用變數出範圍後,堆棧中的引用會無效,當託管堆中的執行個體還在,直到垃圾收集器對其進行清理。

到這裡細心的讀者可能會有些疑問,是不是定義參考型別時,電腦要搜尋整個堆,尋找足夠大的記憶體空間來儲存物件呢?這樣會不會效率很低?如果沒有足夠大的連續的空間呢?這個就要談到“託管”了。堆是受垃圾收集器管理的,.net在執行垃圾收集器時釋放能釋放的所有對象,並壓縮其他對象,然後把所有自由空間組合在一起移動到堆的頂端, 形成連續的塊,同時更新其他移動對象的引用。如果再有對象定義,可以很快找到合適的空間。如此看來託管堆工作方式與堆棧類似,它是通過堆積指標來完成空間的分配和回收的。

上面談了.net對記憶體空間分配的管理過程和方式,接下來談一談對記憶體的回收過程。談到資源的清理,不得不提到的兩個概念和三個方法。

兩個概念為:託管資源和非託管資源。

託管資源接受.net framework的CLR(通用語言運行時)的管理;非託管資源則不受它的管理。

三個方法為:Finalize(),Dispose(),Close()。

一、Finalize()為析構方法,清除非託管資源。

在類中定義方式:

public ClassName{

~ClassName()

{

//清理非託管資源(如關閉檔案和資料庫聯結等)

}

}

它的特點是:

1. 運行不確定性。

它是受垃圾收集器的管理,當垃圾收集器工作時,會調用此方法。

2. 效能開銷大。

垃圾收集器工作方式為,對象如果執行了Finalize()方法,垃圾收集器第一次執行時,會把它放在一個特殊的隊列中;第二次執行的時候才會刪除此對象。

3. 不能顯示定義和調用,定義為析構方法形式。

基於以上特點,最好不要執行Finalize()方法,除非類確實需要它或與其他兩個方法結合來用。

二、Dispose()方法,可清除一切需要清除的資源,包括託管和非託管資源。

定義如下:

public void Dispose()

{

//清理應該清理的資源(包括託管和非託管資源)

System.GC.SuppressFinalize(this); //這一句很重要,下面會解釋原因。

}

它的特點:

1. 任何客戶代碼都應顯示調用這個方法,來釋放資源。

2. 由於第一點的原因,一般要做一個備份,這個備份一般由析構方法來擔任角色。

3. 定義此方法的類,必須繼承IDisposable介面。

4. 文法關鍵字using等同於調用Dispose(),使用using時,它是預設調用Dispose()方法。所以使用using的類也要繼承IDisposable介面。

Dispose()方法比較靈活,在資源不需要時立即釋放。它是資源的最終處理,調用它意味著會最終刪除對象。

三、Close()方法,暫時處置資源的狀態,可能以後還會使用。一般處理非託管資源。

定義如下:

public viod Close()

{

//對非託管資源狀態的設定,如關閉檔案或資料庫連接

}

它的特點:

對非託管資源狀態的設定,一般是關閉檔案或資料庫連接。


下面寫一個綜合且又經典的的例子,利用代碼示範一下各部分的作用:(為了省事,這個例子是從網上杜撰來的,只要說明問題就可以了,你說呢。在此應該感謝代碼原創者,感謝他寫了這麼經典和易懂的代碼,我們受益匪淺!)


public class ResourceHolder : System.IDisposable

{

public void Dispose()

{

Dispose(true);

System.GC.SuppressFinalize(this);

// 上面一行代碼作用是防止"記憶體回收行程"調用這個類中的析構方法

// " ~ResourceHolder() "

// 為什麼要防止呢? 因為如果使用者記得調用Dispose()方法,那麼

// "記憶體回收行程"就沒有必要"多此一舉"地再去釋放一遍"非託管資源"了

// 如果使用者不記得調用呢,就讓"記憶體回收行程"幫我們去"多此一舉"吧 ^_^

// 你看不懂我上面說的不要緊,下面我還有更詳細的解釋呢!

}


protected virtual void Dispose(bool disposing)

{

if (disposing)

{

// 這裡是清理"託管資源"的使用者程式碼片段。

}

// 這裡是清理"非託管資源"的使用者程式碼片段。此處為析構方法的實際執行代碼,為了避免客戶代碼忘記顯示調用Dispose()方法,所作的備份。

}


~ResourceHolder()

{

Dispose(false); // 這裡是清理"非託管資源"

}

}


如果看不明白以上代碼,一定要仔細閱讀以下解釋,很經典,不看會後悔呦。

這裡,我們必須要清楚,需要使用者調用的是方法Dispose()而不是方法Dispose(bool),然而,這裡真正執行釋放工作的方法卻並不是Dispose(),而是Dispose(bool) ! 為什麼呢?仔細看代碼,在Dispose()中,調用了Dispose(true),而參數為"true"時,作用是清理所有的託管資源和非託管資源;大家一定還記得我前面才說過,"使用析構方法是用來釋放非託管資源的",那麼這裡既然Dispose()可以完成釋放非託管資源的工作,還要析構方法幹什麼呢? 其實,析構方法的作用僅僅是一個"備份"!

為什麼呢?

格地說,凡執行了介面"IDisposable"的類,那麼只要程式員在代碼中使用了這個類的對象執行個體,那麼早晚得調用這個類的Dispose()方法,同時,如果類中含有對非託管資源的使用,那麼也必須釋放非託管資源! 可惜,如果釋放非託管資源的代碼放在析構方法中(上面的例子對應的是 " ~ResourceHolder() "),那麼程式員想調用這段釋放代碼是不可能做到的(因為析構方法不能被使用者調用,只能被系統,確切說是"記憶體回收行程"調用),所以大家應該知道為什麼上面例子中"清理非託管資源的使用者程式碼片段"是在Dispose(bool)中,而不是~ResourceHolder()中! 不過不幸的是,並不是所有的程式員都時刻小心地記得調用Dispose()方法,萬一程式員忘記調用此方法,託管資源當然沒問題,早晚會有"記憶體回收行程"來回收(只不過會延遲一會兒),那麼非託管資源呢?它可不受CLR的控制啊!難道它所佔用的非託管資源就永遠不能釋放了嗎? 當然不是!我們還有"析構方法"呢! 如果忘記調用Dispose(),那麼"記憶體回收行程"也會調用"析構方法"來釋放非託管資源的!(多說一句廢話,如果程式員記得調用Dispose()的話,那麼代碼"System.GC.SuppressFinalize(this);"則可以防止"記憶體回收行程"調用析構方法,這樣就不必多釋放一次"非託管資源"了) 所以我們就不怕程式員忘記調用Dispose()方法了。

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.