C# GC.Collect()

 用C#寫了一個運用ICE組件進行接口通信的服務程序,程序運行很正常,可是在客戶端調用ICE接口時出現了大量的數據丟失,而且偶爾還通信不上,服務端最明顯的現象就是telnet服務的通信端口時不通(cmd窗口一閃而過),經過大量時間的跟蹤測試,最終只能通過tfs上的歷史修改記錄來一步一步恢復還原,最後問題定位在GC.Collect();這一句代碼上:大部份接口都存在這一句代碼進行內存回收,而把這句注掉以後,通信和數據傳輸都正常下來了!

    MSDN對於強制垃圾回收的解釋:

    垃圾回收 GC 類提供 GC.Collect 方法,您可以使用該方法讓應用程序在一定程度上直接控制垃圾回收器。通常情況下,您應該避免調用任何回收方法,讓垃圾回收器獨立運行。在大多數情況下,垃圾回收器在確定執行回收的最佳時機方面更有優勢。但是,在某些不常發生的情況下,強制回收可以提高應用程序的性能。當應用程序代碼中某個確定的點上使用的內存量大量減少時,在這種情況下使用 GC.Collect 方法可能比較合適。例如,應用程序可能使用引用大量非託管資源的文檔。當您的應用程序關閉該文檔時,您完全知道已經不再需要文檔曾使用的資源了。出於性能的原因,一次全部釋放這些資源很有意義。有關更多信息,請參見 GC.Collect 方法。

    在垃圾回收器執行回收之前,它會掛起當前正在執行的所有線程。如果不必要地多次調用 GC.Collect,這可能會造成性能問題。您還應該注意不要將調用 GC.Collect 的代碼放置在程序中用戶可以經常調用的點上。這可能會削弱垃圾回收器中優化引擎的作用,而垃圾回收器可以確定運行垃圾回收的最佳時間。

    另外做一些擴展閱讀:

GC工作方式

首先我們要知道託管代碼中的對象什麼時候回收我們管不了(除非用GC.Collect強迫GC回收,這不推薦,後面會說明爲什麼)。GC會在它"高興"的時候執行一次回收(這有許多原因,比如內存不夠用時。這樣做是爲了提高內存分配、回收的效率)。那麼如果我們用Destructor呢?同樣不行,因爲.NET中Destructor的概念已經不存在了,它變成了Finalizer,這會在後面講到。目前請記住一個對象只有在沒有任何引用的情況下才能夠被回收。爲了說明這一點請看下面這一段代碼:[C#]

object objA = new object();

 object objB = objA;

 objA = null;

 // 強迫回收。

 GC.Collect();

 objB.ToString();


 [Visual Basic]

 Dim objA As New Object()

 Dim objB As Object = objA

 objA = Nothing

 ' 強迫回收。

 GC.Collect()objB.ToString()


這裏objA引用的對象並沒有被回收,因爲這個對象還有另一個引用,ObjB。對象在沒有任何引用後就有條件被回收了。當GC回收時,它會做以下幾步:確定對象沒有任何引用。檢查對象是否在Finalizer表上有記錄。如果在Finalizer表上有記錄,那麼將記錄移到另外的一張表上,在這裏我們叫它Finalizer2。如果不在Finalizer2表上有記錄,那麼釋放內存。在Finalizer2表上的對象的Finalizer會在另外一個low priority的線程上執行後從表上刪除。當對象被創建時GC會檢查對象是否有Finalizer,如果有就會在Finalizer表中添加紀錄。我們這裏所說的記錄其實就是指針。如果仔細看這幾個步驟,我們就會發現有Finalizer的對象第一次不會被回收,也就是,有Finalizer的對象要一次以上的Collect操作纔會被回收,這樣就要慢一步,所以作者推薦除非是絕對需要不要創建Finalizer。爲了證明GC確實這麼工作而不是作者胡說,我們將在對象的復活一章中給出一個示例,眼見爲實,耳聽爲虛嘛!^_^GC爲了提高回收的效率使用了Generation的概念,原理是這樣的,第一次回收之前創建的對象屬於Generation 0,之後,每次回收時這個Generation的號碼就會向後挪一,也就是說,第二次回收時原來的Generation 0變成了Generation 1,而在第一次回收後和第二次回收前創建的對象將屬於Generation 0。GC會先試着在屬於Generation 0的對象中回收,因爲這些是最新的,所以最有可能會被回收,比如一些函數中的局部變量在退出函數時就沒有引用了(可被回收)。如果在Generation 0中回收了足夠的內存,那麼GC就不會再接着回收了,如果回收的還不夠,那麼GC就試着在Generation 1中回收,如果還不夠就在Generation 2中回收,以此類推。Generation也有個最大限制,根據Framework版本而定,可以用GC.MaxGeneration獲得。在回收了內存之後GC會重新排整內存,讓數據間沒有空格,這樣是因爲CLR順序分配內存,所以內存之間不能有空着的內存。現在我們知道每次回收時都會浪費一定的CPU時間,這就是我說的一般不要手動GC.Collect的原因(除非你也像我一樣,寫一些有關GC的示例!^_^)。‍
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章