C# 資源釋放

來源:互聯網
上載者:User
文章目錄
  • C# 資源釋放
C# 資源釋放終於開始動手寫這篇文章了,有個網友催了我好幾次,今天終於可以靜下心來完成它。 便於對文章的開展,需要先明確兩個概念。第一個就是很多人用.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 調用,因此解構函式可以作為一個補救方法。而Close 與Dispose 這兩種方法的區別在於,調用完了對象的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]--> 對於Dispose 和Close 方法來說,需要看所定義的類型所使用的資源(參看前面所說),而決定是否去定義這兩個函數;<!--[if !supportLists]--> 3.<!--[endif]--> 在實現Dispose 方法的時候,一定要加上“ GC.SuppressFinalize( this ) ”語句,避免再讓GC 調用對象的解構函式。 C#程式所使用的記憶體是受託管的,但不意味著濫用,好地編程習慣有利於提高代碼的品質 以及程式的運行效率。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.