C#基礎知識回顧:1.由WeakReference想到物件的建立與銷燬

生命夢想發表於2018-05-24

.Net Framework中,把資源分為託管資源和非託管資源兩大類,

託管資源指可以通過.Net Frame垃圾回收器進行回收的資源,主要是指分配在託管堆上你的記憶體資源,這類資源的回收是不需要人工干預,.Net Framework的垃圾回收器會在合適的時刻進行回收,程式也可以主動呼叫GC.Collect()強制執行垃圾回收但是不建議

非託管資源則是指不能被.Net Framework垃圾回收器回收的資源,主要包括以下幾類:檔案讀寫、視窗操作、網路連線、資料庫連線、GDI畫刷、圖示等。針對這類資源,垃圾回收器能夠跟蹤其生存期,但不瞭解具體如何清理這些資源。針對這類資源,垃圾回收器在清理的時候回撥用Object.Finalize()方法,該方法是虛方法,非託管物件需要重寫方法來實現資源回收。

託管物件是不能過載Object.Finalize()方法,編譯器會自動根據解構函式生成對應的Object.Finalize()方法,因此,當託管物件中使用到非託管資源時,需要在解構函式中釋放該資料。

解構函式的定義如下(在方法名稱為類名稱前加了一個“~”):

    public class TestA
    {
        public string Name = "TestA";
        ~TestA()
        {
            Console.WriteLine("Dispose TestA");
        }
    }

 

由於解構函式是有垃圾回收器主動呼叫的,因此,呼叫該方法前,類包含的託管物件可能已經被主動回收了,此時再進行釋放操作,可能會造成異常,因此,不建議在解構函式中釋放託管資源。

垃圾回收器的操作時機是.NetFramework自己所決定的,它會在系統記憶體的佔用和系統系統之間作一個平衡來決定何時出發,因此,對於非託管資源,這類資源屬於寶貴資源,必須及時地釋放掉,不能有垃圾回收器來決定何時釋放。

.Net Framework中為回收資源,而定義了一個介面,IDisposable定義為:

 1     //
 2     // 摘要:
 3     //     Defines a method to release allocated resources.
 4     [ComVisible(true)]
 5     public interface IDisposable
 6     {
 7         //
 8         // 摘要:
 9         //     Performs application-defined tasks associated with freeing, releasing, or resetting
10         //     unmanaged resources.
11         void Dispose();
12     }

任何包括非託管資源的類都需要繼承此介面,並在介面方法中實現對託管資源和非託管資源的釋放操作,同時在解構函式中實現對非託管資源的釋放。

這麼實現的好處就是:程式碼中顯示呼叫Dispose()時,能夠主動地釋放資源(託管和非託管),垃圾回收器就會移除該物件不會執行回收操作(即不再執行Finalize()方法),從而提高了效能。而如果沒有顯示呼叫Dispose(),垃圾回收器也會在適當的時機呼叫Finalize()方法來釋放非託管資源。

C#語言中using關鍵字作為語法糖出現,將非託管資源的使用包含在using()中使用,執行完成後會主動呼叫Dispose方法來釋放資源。

基於效能考慮,應該降低使用解構函式來釋放資源的使用頻率,因此,沒有解構函式的物件在垃圾處理器中直接刪除,而包括解構函式的物件,則需要先執行解構函式,然後刪除物件,增加了一次操作,會降低垃圾回收器的工作效率,從而影響效能。因此,對於非託管物件的釋放,應該實現IDispose介面來回收資源,而不依賴垃圾回收器。

當前類使用new關鍵字建立了另一個類的例項時,當前類對新建的類例項的引用稱為強引用,當新建類例項沒有釋放時,垃圾回收器則無法回收這個物件。只有將該引用釋放的時候,垃圾回收器才會回收該物件。

1 TestA a = new TestA();
2 a = null;
3 GC.Collect();
4 //當把變數a置為null,主動執行垃圾回收後就會回收該物件。

程式設計過程,我們會遇到這樣的場景,持有一個物件例項的引用,這個例項比較龐大且建立成本不是太高,但是使用頻率比較低。該情況下,可以使用WeakReference對這個例項進行包裝,當需要使用當物件時,先呼叫WeakReference.IsAlive是否等於true或者Target不等於null,判斷該物件例項是否存在,若存在則呼叫其Target並強制轉換後使用,若不存在,則需要重新建立一個新的例項來使用。使用WeakReference包括的物件例項,垃圾回收器會釋放該例項,回收其記憶體空間。所用,使用前必須判斷其是否仍然存在。

相關文章