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接口,代碼如下:


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


運作結果如下,可以看到資源運作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