從C#記憶體回收(GC)機制中挖掘效能最佳化方案

來源:互聯網
上載者:User

標籤:style   blog   http   color   io   os   使用   ar   java   

  GC,Garbage Collect,中文意思就是記憶體回收,指的是系統中的記憶體的分配和回收管理。其對系統效能的影響是不可小覷的。今天就來說一下關於GC最佳化的東西,這裡並不著重說概念和理論,主要說一些實用的東西。關於概念和理論這裡只做簡單說明,具體的大家可以看微軟官方文檔。

一、什麼是GC                                                                                             

  GC如其名,就是垃圾收集,當然這裡僅就記憶體而言。Garbage Collector(垃圾收集器,在不至於混淆的情況下也成為GC)以應用程式的root為基礎,遍曆應用程式在Heap上動態分配的所有對象[2],通過識別它們是否被引用來確定哪些對象是已經死亡的、哪些仍需要被使用。已經不再被應用程式的root或者別的對象所引用的對象就是已經死亡的對象,即所謂的垃圾,需要被回收。這就是GC工作的原理。為了實現這個原理,GC有多種演算法。比較常見的演算法有Reference Counting,Mark Sweep,Copy Collection等等。目前主流的虛擬系統.NET CLR,Java VM和Rotor都是採用的Mark Sweep演算法。(此段內容來自網路)

.NET的GC機制有這樣兩個問題:

  首先,GC並不是能釋放所有的資源。它不能自動釋放非託管資源。

  第二,GC並不是即時性的,這將會造成系統效能上的瓶頸和不確定性。

  GC並不是即時性的,這會造成系統效能上的瓶頸和不確定性。所以有了IDisposable介面,IDisposable介面定義了Dispose方法,這個方法用來供程式員顯式調用以釋放非託管資源。使用using語句可以簡化資源管理。

 

二、託管資源和非託管資源                                                                           

  託管資源指的是.NET可以自動進行回收的資源,主要是指託管堆上分配的記憶體資源。託管資源的回收工作是不需要人工幹預的,有.NET運行庫在合適調用記憶體回收行程進行回收。

      非託管資源指的是.NET不知道如何回收的資源,最常見的一類非託管資源是封裝作業系統資源的對象,例如檔案,視窗,網路連接,資料庫連接,畫刷,表徵圖等。這類資源,記憶體回收行程在清理的時候會調用Object.Finalize()方法。預設情況下,方法是空的,對於非託管對象,需要在此方法中編寫回收非託管資源的代碼,以便記憶體回收行程正確回收資源。

         在.NET中,Object.Finalize()方法是無法重載的,編譯器是根據類的解構函式來自動產生Object.Finalize()方法的,所以對於包含非託管資源的類,可以將釋放非託管資源的代碼放在解構函式。

 

三、關於GC最佳化的一個例子                                                                          

  正常情況下,我們是不需要去管GC這些東西的,然而GC並不是即時性的,所以我們的資源使用完後,GC什麼時候回收也是不確定的,所以會帶來一些諸如記憶體流失、記憶體不足的情況,比如我們處理一個約500M的大檔案,用完後GC不會立刻執行清理來釋放記憶體,因為GC不知道我們是否還會使用,所以它就等待,先去處理其他的東西,過一段時間後,發現這些東西不再用了,才執行清理,釋放記憶體。

  下面,來介紹一下GC中用到的幾個函數:

  GC.SuppressFinalize(this); //請求通用語言執行平台不要調用指定對象的終結器。

  GC.GetTotalMemory(false); //檢索當前認為要分配的位元組數。 一個參數,指示此方法是否可以等待較短間隔再返回,以便系統回收垃圾和終結對象。

  GC.Collect();  //強制對所有代進行即時記憶體回收。

GC運行機制

  寫代碼前,我們先來說一下GC的運行機制。大家都知道GC是一個後台線程,他會周期性的尋找對象,然後調用Finalize()方法去消耗他,我們繼承IDispose介面,調用Dispose方法,銷毀了對象,而GC並不知道。GC依然會調用Finalize()方法,而在.NET 中Object.Finalize()方法是無法重載的,所以我們可以使用解構函式來阻止重複的釋放。我們調用完Dispose方法後,還有調用GC.SuppressFinalize(this) 方法來告訴GC,不需要在調用這些對象的Finalize()方法了。

  下面,我們建立一個控制台程式,加一個Factory類,讓他繼承自IDispose介面,代碼如下:

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace GarbageCollect{   public class Factory : IDisposable   {      private StringBuilder sb = new StringBuilder();      List<int> list = new List<int>();      //拼接字串,創造一些記憶體垃圾      public void MakeSomeGarbage()      {         for (int i = 0; i < 50000; i++)         {            sb.Append(i.ToString());         }      }      //銷毀類時,會調用解構函式      ~Factory()      {         Dispose(false);      }      public void Dispose()      {         Dispose(true);      }      protected virtual void Dispose(bool disposing)      {         if (!disposing)         {            return;         }         sb = null;         GC.Collect();         GC.SuppressFinalize(this);      }   }}

只有繼承自IDispose介面,使用這個類時才能使用Using語句,在main方法中寫如下代碼:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Diagnostics;namespace GarbageCollect{   class Program   {      static void Main(string[] args)      {         using(Factory f = new Factory())         {            f.MakeSomeGarbage();            Console.WriteLine("Total memory is {0} KBs.", GC.GetTotalMemory(false) / 1024);         }         Console.WriteLine("After GC total memory is {0} KBs.", GC.GetTotalMemory(false) / 1024);                  Console.Read();      }   }}

運行結果如下,可以看到資源運行MakeSomeGarbage()函數後的記憶體佔用為1796KB,釋放後成了83Kb.

 

代碼運行機制:

  我們寫了Dispose方法,還寫了解構函式,那麼他們分別什麼時候被調用呢?我們分別在兩個方法上面下斷點。調試運行,你會發現先走到了Dispose方法上面,知道程式運行完也沒走解構函式,那是因為我們調用了GC.SuppressFinalize(this)方法,如果去掉這個方法後,你會發現先走Dispose方法,後面又走解構函式。所以,我們可以得知,如果我們調用Dispose方法,GC就會調用解構函式去銷毀對象,從而釋放資源。

 

四、什麼時候該調用GC.Collect                                                                      

  這裡為了讓大家看到效果,我顯示調用的GC.Collect()方法,讓GC立刻釋放記憶體,但是頻繁的調用GC.Collect()方法會降低程式的效能,除非我們程式中某些操作佔用了大量記憶體需要馬上釋放,才可以顯示調用。下面是官方文檔中的說明:

  記憶體回收 GC 類提供 GC.Collect 方法,您可以使用該方法讓應用程式在一定程度上直接控制記憶體回收行程。通常情況下,您應該避免調用任何回收方法,讓記憶體回收行程獨立運行。在大多數情況下,記憶體回收行程在確定執行回收的最佳時機方面更有優勢。但是,在某些不常發生的情況下,強制回收可以提高應用程式的效能。當應用程式代碼中某個確定的點上使用的記憶體量大量減少時,在這種情況下使用 GC.Collect 方法可能比較合適。例如,應用程式可能使用引用大量非託管資源的文檔。當您的應用程式關閉該文檔時,您完全知道已經不再需要文檔曾使用的資源了。出於效能的原因,一次全部釋放這些資源很有意義。有關更多資訊,請參見 GC.Collect 方法。
  在記憶體回收行程執行回收之前,它會掛起當前正在執行的所有線程。如果不必要地多次調用 GC.Collect,這可能會造成效能問題。您還應該注意不要將調用GC.Collect 的代碼放置在程式中使用者可以經常調用的點上。這可能會削弱記憶體回收行程中最佳化引擎的作用,而記憶體回收行程可以確定運行記憶體回收的最佳時間。

 

參考資料:http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface

 

從C#記憶體回收(GC)機制中挖掘效能最佳化方案

聯繫我們

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