GC,Garbage Collect,中文意思就是垃圾回收,指的是系統中的內存的分配和回收管理。其對系統性能的影響是不可小覷的。今天就來講一下關於GC優化的東西,這裏並不着重說概念和理論,主要說一些實用的東西。關於概念和理論這裏只作簡單說明html
GC如其名,就是垃圾收集,固然這裏僅就內存而言。Garbage Collector(垃圾收集器,在不至於混淆的狀況下也成爲GC)以應用程序的root爲基礎,遍歷應用程序在Heap上動態分配的全部對象[2],經過識別它們是否被引用來肯定哪些對象是已經死亡的、哪些仍須要被使用。已經再也不被應用程序的root或者別的對象所引用的對象就是已經死亡的對象,即所謂的垃圾,須要被回收。這就是GC工做的原理。爲了實現這個原理,GC有多種算法。比較常見的算法有Reference Counting,Mark Sweep,Copy Collection等等。目前主流的虛擬系統.NET CLR,Java VM和Rotor都是採用的Mark Sweep算法。(此段內容來自網絡)程序員
首先,GC並非能釋放全部的資源。它不能自動釋放非託管資源。算法
第二,GC並非實時性的,這將會形成系統性能上的瓶頸和不肯定性。數據庫
GC並非實時性的,這會形成系統性能上的瓶頸和不肯定性。因此有了IDisposable接口,IDisposable接口定義了Dispose方法,這個方法用來供程序員顯式調用以釋放非託管資源。使用using語句能夠簡化資源管理。網絡
託管資源指的是.NET能夠自動進行回收的資源,主要是指託管堆上分配的內存資源。託管資源的回收工做是不須要人工干預的,有.NET運行庫在合適調用垃圾回收器進行回收。函數
非託管資源指的是.NET不知道如何回收的資源,最多見的一類非託管資源是包裝操做系統資源的對象,例如文件,窗口,網絡鏈接,數據庫鏈接,畫刷,圖標等。這類資源,垃圾回收器在清理的時候會調用Object.Finalize()方法。默認狀況下,方法是空的,對於非託管對象,須要在此方法中編寫回收非託管資源的代碼,以便垃圾回收器正確回收資源。性能
在.NET中,Object.Finalize()方法是沒法重載的,編譯器是根據類的析構函數來自動生成Object.Finalize()方法的,因此對於包含非託管資源的類,能夠將釋放非託管資源的代碼放在析構函數。優化
正常狀況下,咱們是不須要去管GC這些東西的,然而GC並非實時性的,因此咱們的資源使用完後,GC何時回收也是不肯定的,因此會帶來一些諸如內存泄漏、內存不足的狀況,好比咱們處理一個約500M的大文件,用完後GC不會馬上執行清理來釋放內存,由於GC不知道咱們是否還會使用,因此它就等待,先去處理其餘的東西,過一段時間後,發現這些東西再也不用了,才執行清理,釋放內存。ui
下面,來介紹一下GC中用到的幾個函數:this
GC.SuppressFinalize(this); //請求公共語言運行時不要調用指定對象的終結器。
GC.GetTotalMemory(false); //檢索當前認爲要分配的字節數。 一個參數,指示此方法是否能夠等待較短間隔再返回,以便系統回收垃圾和終結對象。
GC.Collect(); //強制對全部代進行即時垃圾回收。
寫代碼前,咱們先來講一下GC的運行機制。你們都知道GC是一個後臺線程,他會週期性的查找對象,而後調用Finalize()方法去消耗他,咱們繼承IDispose接口,調用Dispose方法,銷燬了對象,而GC並不知道。GC依然會調用Finalize()方法,而在.NET 中Object.Finalize()方法是沒法重載的,因此咱們可使用析構函數來阻止重複的釋放。咱們調用完Dispose方法後,還有調用GC.SuppressFinalize(this) 方法來告訴GC,不須要在調用這些對象的Finalize()方法了。
下面,咱們新建一個控制檯程序,加一個Factory類,讓他繼承自IDispose接口,代碼以下:
public class Factory : IDisposable { private StringBuilder sb = new StringBuilder(); List list = new List(); //拼接字符串,創造一些內存垃圾 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方法中寫以下代碼:
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馬上釋放內存,可是頻繁的調用GC.Collect()方法會下降程序的性能,除非咱們程序中某些操做佔用了大量內存須要立刻釋放,才能夠顯示調用。下面是官方文檔中的說明:
垃圾回收 GC 類提供 GC.Collect 方法,您可使用該方法讓應用程序在必定程度上直接控制垃圾回收器。一般狀況下,您應該避免調用任何回收方法,讓垃圾回收器獨立運行。在大多數狀況下,垃圾回收器在肯定執行回收的最佳時機方面更有優點。可是,在某些不常發生的狀況下,強制回收能夠提升應用程序的性能。當應用程序代碼中某個肯定的點上使用的內存量大量減小時,在這種狀況下使用 GC.Collect 方法可能比較合適。例如,應用程序可能使用引用大量非託管資源的文檔。當您的應用程序關閉該文檔時,您徹底知道已經再也不須要文檔曾使用的資源了。出於性能的緣由,一次所有釋放這些資源頗有意義。有關更多信息,請參見 GC.Collect 方法。 在垃圾回收器執行回收以前,它會掛起當前正在執行的全部線程。若是沒必要要地屢次調用 GC.Collect,這可能會形成性能問題。您還應該注意不要將調用GC.Collect 的代碼放置在程序中用戶能夠常常調用的點上。這可能會削弱垃圾回收器中優化引擎的做用,而垃圾回收器能夠肯定運行垃圾回收的最佳時間。