posts - 5,  comments - 5,  trackbacks - 0

          終于開始動手寫這篇文章了,有個網友催了我好幾次,今天終于可以靜下心來完成它。

          便于對文章的開展,需要先明確兩個概念。

          第一個就是很多人用.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提供了三種方法,也是最常見的三種,大致如下:

          <!--[if !supportLists]-->1. <!--[endif]-->析構函數;

          <!--[if !supportLists]-->2. <!--[endif]-->繼承IDisposable接口,實現Dispose方法;

          <!--[if !supportLists]-->3. <!--[endif]-->提供Close方法。

          經過前面的介紹,可以知道析構函數只能被GC來調用的,那么無法確定它什么時候被調用,因此用它作為資源的釋放并不是很合理,因為資源釋放不及時;但是為了防止資源泄漏,畢竟它會被GC調用,因此析構函數可以作為一個補救方法。而CloseDispose這兩種方法的區別在于,調用完了對象的Close方法后,此對象有可能被重新進行使用;而Dispose方法來說,此對象所占有的資源需要被標記為無用了,也就是此對象被銷毀了,不能再被使用。例如,常見SqlConnection這個類,當調用完Close方法后,可以通過Open重新打開數據庫連接,當徹底不用這個對象了就可以調用Dispose方法來標記此對象無用,等待GC回收。明白了這兩種方法的意思后,大家在往自己的類中添加的接口時候,不要歪曲了這兩者意思。

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

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

              ///<summary>

              /// The class to show three disposal function

              ///</summary>

              public class DisposeClass:IDisposable

              {

                  public void Close()

                  {

                      Debug.WriteLine( "Close called!" );

                  }

                  ~DisposeClass()

                  {

                      Debug.WriteLine( "Destructor called!" );

                  }

                  #region IDisposable Members

                  public void Dispose()

                  {

                      // TODO: Add DisposeClass.Dispose implementation

                      Debug.WriteLine( "Dispose called!" );

                  }

                  #endregion

              }

          對于Close來說不屬于真正意義上的釋放,除了注意它需要顯示被調用外,我在此對它不多說了。而對于析構函數而言,不是在對象離開作用域后立刻被執行,只有在關閉進程或者調用GC.Collect方法的時候才被調用,參看如下的代碼運行結果。

                  private void Create()

                  {

                      DisposeClass myClass = new DisposeClass();

                  }

                  private void CallGC()

                  {

                      GC.Collect();

                  }

                  // Show destructor

                  Create();

                  Debug.WriteLine( "After created!" );

                  CallGC();

          運行的結果為:

          After created!

          Destructor called!

          顯然在出了Create函數外,myClass對象的析構函數沒有被立刻調用,而是等顯示調用GC.Collect才被調用。

          對于Dispose來說,也需要顯示的調用,但是對于繼承了IDisposable的類型對象可以使用using這個關鍵字,這樣對象的Dispose方法在出了using范圍后會被自動調用。例如:

              using( DisposeClass myClass = new DisposeClass() )

              {

                  //other operation here

              }

          如上運行的結果如下:

          Dispose called!

          那么對于如上DisposeClass類型的Dispose實現來說,事實上GC還需要調用對象的析構函數,按照前面的GC流程來說,GC對于需要調用析構函數的對象來說,至少經過兩個步驟,即首先調用對象的析構函數,其次回收內存。也就是說,按照上面所寫的Dispose函數,雖說被執行了,但是GC還是需要執行析構函數,那么一個完整的Dispose函數,應該通過調用GC.SuppressFinalize(this )來告訴GC,讓它不用再調用對象的析構函數中。那么改寫后的DisposeClass如下:

              ///<summary>

              /// The class to show three disposal function

              ///</summary>

              public class DisposeClass:IDisposable

              {

                  public void Close()

                  {

                      Debug.WriteLine( "Close called!" );

                  }

                  ~DisposeClass()

                  {

                      Debug.WriteLine( "Destructor called!" );

                  }

                  #region IDisposable Members

                  public void Dispose()

                  {

                      // TODO: Add DisposeClass.Dispose implementation

                      Debug.WriteLine( "Dispose called!" );

                      GC.SuppressFinalize( this );

                  }

                  #endregion

              }

          通過如下的代碼進行測試。

                  private void Run()

                  {

                      using( DisposeClass myClass = new DisposeClass() )

                      {

                          //other operation here

                      }

                  }

                  private void CallGC()

                  {

                      GC.Collect();

                  }

                  // Show destructor

                  Run();

                  Debug.WriteLine( "After Run!" );

                  CallGC();

          運行的結果如下:

          Dispose called!

          After Run!

          顯然對象的析構函數沒有被調用。通過如上的實驗以及文字說明,大家會得到如下的一個對比表格。

          析構函數

          Dispose方法

          Close方法

          意義

          銷毀對象

          銷毀對象

          關閉對象資源

          調用方式

          不能被顯示調用,會被GC調用

          需要顯示調用

          或者通過using語句

          需要顯示調用

          調用時機

          不確定

          確定,在顯示調用或者離開using程序塊

          確定,在顯示調用時

          那么在定義一個類型的時候,是否一定要給出這三個函數地實現呢。

          我的建議大致如下。

          <!--[if !supportLists]-->1.<!--[endif]-->提供析構函數,避免資源未被釋放,主要是指非內存資源;

          <!--[if !supportLists]-->2.<!--[endif]-->對于DisposeClose方法來說,需要看所定義的類型所使用的資源(參看前面所說),而決定是否去定義這兩個函數;

          <!--[if !supportLists]-->3.<!--[endif]-->在實現Dispose方法的時候,一定要加上“GC.SuppressFinalize( this )”語句,避免再讓GC調用對象的析構函數。

          C#程序所使用的內存是受托管的,但不意味著濫用,好地編程習慣有利于提高代碼的質量以及程序的運行效率。

          posted on 2008-06-03 17:41 曾科 閱讀(84) 評論(0)  編輯  收藏 所屬分類: ASP.NET
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(3)

          隨筆檔案

          文章分類

          文章檔案

          相冊

          .net

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 武冈市| 左云县| 平武县| 阿合奇县| 从化市| 连平县| 连城县| 凤庆县| 梅河口市| 灵川县| 中山市| 朝阳区| 德惠市| 沁源县| 区。| 永靖县| 古丈县| 始兴县| 阿拉善左旗| 韶关市| 丹阳市| 商南县| 寿宁县| 嵩明县| 兰西县| 彝良县| 翼城县| 阿克| 盐边县| 广宗县| 婺源县| 广汉市| 石泉县| 敦煌市| 凌源市| 进贤县| 潜山县| 理塘县| 白玉县| 古蔺县| 宁乡县|