.NET的GC機制

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

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

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

垃圾回收的優缺點

實現垃圾回收有多種方法,每一種方法都提供了不同的性能特徵。然而,所有的垃圾回收系統都具有一個共同的、與手工方法相對的屬性。垃圾回收最主要的優點是簡單和安全。
在垃圾回收環境中,可以顯式地使用new分配內存,但是不需要顯式地釋放內存。相反,不再使用的內存會被自動回收。因此,不可能會忘記釋放對象或者過早地釋放對象。
這樣做簡化了程序設計,並且阻止了有問題的類。另外,不可能會意外地兩次釋放動態分配的內存。因此,垃圾回收爲內存管理問題 提供了一種易於使用的、不容易犯錯的、
可靠的解決方案。

遺憾的是,垃圾回收的簡單及安全性是有代價的。第一個代價是垃圾回收機制引起的開銷。所有垃圾回收的配置都會消耗一些CPU資源,因爲這種不再使用的內存的回收並不是
一個免費過程。當使用手工方法的時候,不會有這樣的開銷。

第二個代價是在銷燬對象時容易失控。使用手工方法時,當對對象執行delete語句的時候,及時地銷燬這個對象(和所調用的它的析構函數),而垃圾回收沒有這種切實而快速
的規則。相反,當使用垃圾回收時,直到回收器運行並回收對象的時候,對象纔會被銷燬,而回收器只有在某個特定時刻纔會運行。例如, 回收器只有在自由內存的數量低於
某個值的時候纔會運行。另外,用戶並不能總是知道垃圾回收器銷燬對象的順序和時間。在某些情況下,不能準確地知道對象銷燬 的時間會導致一些問題,因爲這意味着程序
不能準確地知道何時爲動態分配的對象調用析構函數。

對於作爲後臺任務運行的垃圾回收系統,這種失控可能會引發某種應用程序潛在的更加嚴重的問題,因爲這樣做將某種本質上不確定的行爲引入到程序中。在 後臺運行的垃圾
回收器實際上在不可預知的某個時刻回收不再使用的內存。例如,回收器通常只有在CPU空閒的時候纔會運行。由於可能從一個程序的運行轉到下 一個程序,從一臺計算機轉到
下一臺計算機,或者從一個操作系統轉到另一個操作系統,因此垃圾回收器在程序中執行的確切位置是不能確定的。對於許多應用程序而言,這並不存在問題,但是對於實時
應用程序這可能會引發災難,因爲在實時應用程序中對垃圾回收器不可預知的CPU循環的分配會導致事件的丟失。

 

爲了解決第一個問題,.NET提供了析構函數,在C#中是~ClassName的形式。如果某個類定義了析構函數,.NET會在第一次的GC中調用析構函數,第二次才真正進行資源釋放。
這就允許了我們能夠做一些手動的資源管理操作,手動對非託管資源進行清理。但是如果沒有必要,定義析構函數就會對性能造成較大的影響。

僅僅依賴析構函數對非託管資源進行釋放是不夠的,這是由於第二個問題:GC並不是實時性的,這會造成系統性能上的瓶頸和不確定性。所以有了IDisposable接口,
IDisposable接口定義了Dispose方法,這個方法用來供程序員顯式調用以釋放非託管資源。

通常我們應該這樣寫程序:

        public class SampleClass : System.IDisposable

        {

                public void Dispose()

                //供程序員顯式調用的Dispose方法

                {

                        Dispose(true);

                        //調用帶參數的Dispose方法,釋放託管和非託管資源

                        System.GC.SuppressFinalize(this);

                        //手動調用了Dispose釋放資源,那麼析構函數就是不必要的了,這裏阻止GC調用析構函數

                }

                protected void Dispose(bool disposing)

                //protected的Dispose方法,保證不會被外部調用。

                //傳入bool值disposing以確定是否釋放託管資源

                {

                        if (disposing)

                        {

                                //在這裏加入清理"託管資源"的代碼,應該是xxx.Dispose();

                        }

                        // 在這裏加入清理"非託管資源"的代碼

                }

                ~SampleClass()

                //供GC調用的析構函數

                {

                        Dispose(false);

                        //釋放非託管資源

                }

        }

        這樣一來,我們就像Delphi裏調用Object.Free方法一樣自然的調用Object.Dispose方法,而即使我們忘記了在合適的時候調用Dispose,GC也會在釋放對象的時候幫
我們清理非託管資源的。GC所充當的角色只是一種保障手段,它應該充當這種角色,我們不能過分依賴它。

        實際上,在較大的模塊退出時我們還應該及時地手動調用GC.Collect進行垃圾回收。
  GC.Collect();
  GC.WaitForPendingFinalizers();

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章