熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

自動內存管理機制深入剖析-C#分析篇

2013-11-13 09:58:37  來源: .NET編程 

  在NET Framework中內存中的資源(即所有二進制信息的集合)分為托管資源非托管資源托管資源必須接受NET Framework的CLR(通用語言運行時)的管理(諸如內存類型安全性檢查)而非托管資源則不必接受NET Framework的CLR管理  (了解更多區別請參閱NET Framework或C#的高級編程資料)

   

  托管資源在NET Framework中又分別存放在兩種地方: 堆棧托管堆(以下簡稱);規則是所有的值類型(包括引用和對象實例)和引用類型的引用都存放在堆棧而所有引用所代表的對象實例都保存在堆中

   

  在C#中釋放托管資源是可以自動通過垃圾回收器完成的(注意垃圾回收機制是NET Framework的特性而不是C#的)但具體來說仍有些需要注意的地方:

   

  值類型(包括引用和對象實例)和引用類型的引用其實是不需要什麼垃圾回收器來釋放內存的因為當它們出了作用域後會自動釋放所占內存(因為它們都保存在堆棧學過數據結構可知這是一種先進後出的結構);

   

  只有引用類型的引用所指向的對象實例才保存在而堆因為是一個自由存儲空間所以它並沒有像堆棧那樣有生存期(堆棧的元素彈出後就代表生存期結束也就代表釋放了內存)並且非常要注意的是垃圾回收器只對這塊區域起作用;

   

  垃圾回收器也許並不像許多人想象的一樣會立即執行(當堆中的資源需要釋放時)而是在引用類型的引用被刪除和它在中的對象實例被刪除中間有個間隔為什麼呢? 因為垃圾回收器的調用是比較消耗系統資源的因此不可能經常被調用!

   

  (當然用戶代碼可以用方法SystemGCCollect()來強制執行垃圾回收器)

   

  然而大多數情況下我們需要明確地在不執行垃圾回收器的情況下釋放托管資源(因為只需要釋放一部分但又是非常需要釋放的資源但最好不要調用垃圾回收器因為垃圾回收器太浪費系統資源了)或需要釋放非托管資源這時候我們該怎麼辦? 這是我們寫代碼的時候必須要考慮的問題(垃圾回收器是系統自動實現的一般情況不需要用戶干預)否則Windows系統會因為內存耗盡而

   

  現在我來告訴怎麼辦那就是使用類的Dispose()方法釋放所有類型資源 和 使用析構方法釋放非托管資源!

   

  Dispose()方法

   

  要通過Dispose()方法來釋放資源那麼在類定義的時候執SystemIDisposable接口然後在類中必須包含這樣定義的方法void Dispose() (在Dispose()方法中就是用戶自己寫的釋放資源的代碼段)這樣一來用戶就會知道可以通過人為地調用Dispose()方法來釋放資源 不過需要注意的是垃圾回收器並不是通過調用Dispose()方法來釋放托管資源的!

   

  析構方法

   

  在C#中定義析構方法的格式是 ~CLASS_NAME() 非常需要注意的是如果一個類中沒有使用到非托管資源那麼請一定不要定義析構方法這是因為對象執行了析構方法那麼垃圾回收器在釋放托管資源之前要先調用析構方法然後第二次才真正釋放托管資源這樣一來兩次刪除動作的花銷比一次大多的! (不過即使你在類中已經定義了析構方法仍然有辦法屏蔽這將在後面的代碼范例中說明) 在析構方法中就是用戶自己寫的釋放非托管資源的代碼段

   

  下面使用一段代碼來示范Dispose()方法和析構方法如何使用:

   

  public class ResourceHolder : SystemIDisposable

  {

  public void Dispose()

  {

  Dispose(true);

  SystemGCSuppressFinalize(this);

  // 上面一行代碼作用是防止垃圾回收器調用這個類中的方法

  // ~ResourceHolder()

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

  // 垃圾回收器就沒有必要多此一舉地再去釋放一遍非托管資源

  // 如果用戶不記得調用呢就讓垃圾回收器幫我們去多此一舉吧 ^_^

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

   

  }

   

  protected virtual void Dispose(bool disposing)

  {

  if (disposing)

  {

  // 這裡是清理托管資源的用戶代碼段

  }

  // 這裡是清理非托管資源的用戶代碼段

  }

   

  ~ResourceHolder()

  {

  Dispose(false);

  }

  }

   

  上面的代碼是一個典型的有兩種Dispose方法的類定義

   

  在NET Framework中有很多系統類是用這種方法定義Dispose()方法的例如:

   

  MSDN中SystemDrawingBrushDispose方法就是這樣定義的:

   

  ************************************************************

  * 釋放由此 Brush 對象使用的所有資源                      *

  * public void Dispose()                                    *

  * 該成員支持 NET 框架結構因此不適用於直接從代碼中使用 *

  * protected virtual void Dispose(bool);                    *

  ************************************************************

   

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

  

  為什麼呢?

   

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

   

  所以我說了這麼一大堆的理由綜合起來只有兩點:

   

  *程序員們啊千萬不要忘記調用Dispose()方法! (如果有的話 ^_^)

   

  *萬一忘記不要著急還有救!!! 因為還有垃圾回收器幫我們自動調用析構方法!


From:http://tw.wingwit.com/Article/program/net/201311/12172.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.