6、對象生命周期和垃圾回收基礎,生命周期垃圾回收
掠過架構化異常處理不談,接下來主要介紹CLR怎樣通過垃圾回收來管理已分配的類實例(對象)。C#程序員從來不直接在內存中刪除一個托管對象,相反,.net對象被分配到了一塊叫做托管堆的內存區域上,到了某個時候他們被垃圾回收期自動銷毀。
及時釋放內部非托管資源:使用System.Object.Fimalize()虛方法和IDisposable接口。
。net4垃圾回收器的新功能:後台垃圾回收和使用System.Lazy<>泛型實現的延遲實例化。
第一個問題,類、對象和引用。
類是一個藍圖,描述這個類型的實例在內存中看起來是什麼樣子的。
對象是類的實例,通過new關鍵字簡歷的單一的,返回一個指向堆上對象的引用,而不是真正的對象本身。
第二個問題,對象生命周期。
規則,當我們建立一個實例,分配在托管堆上之後,就不用再管。等到一個對象從代碼庫的任何部分都不可訪問的時候,垃圾回收器會從堆中刪除它。
要理解,托管隊不只是一個由CLR訪問的隨機內存塊。net垃圾回收器是堆的清潔工,它會壓縮空的內存塊來實現優化(必要的時候)。
注意:
如果我們將對象置為null,編譯器會生成CIL代碼來確保引用不再指向任何對象,但並不強制垃圾回收器立刻將對象移除,如此,在C#中引用置為null意義就不大了。
准確的說,垃圾回收器使用了兩個不同的堆,一個專門用來存儲非常大的對象,這個堆在回收周期較少顧忌,因為要重新定位大對象的性能開銷很大。盡管如此,認為“托管堆”是一個內存區域一般並沒有什麼問題。
對象的代:堆上的每個對象都屬於下列某代。
第0代:從沒有被標記為回收的新分配的對象。
第1代:在上一次垃圾回收中沒有被回收的對象。(也就是,它被標記為回收,但因為已經獲取了足夠的堆空間而沒有被刪除)
第2代:在一次以上的垃圾回收後仍然沒有被回收的對象。
這裡的要點是,通過給堆上的對象賦一個表示代的值,盡快地刪除一些較新的對象(如本地變量),而不會經常“打擾”一些舊對象(例如程序的應用程序對象)。
第三個問題,System.GC。
基礎類庫提供了名為System.GC的類類型,它可以通過編程使用一些靜態成員與垃圾回收器進行交互。這裡要特別注意的是,極少需要在代碼中直接使用這個類。一般情況下,只有在創建那些使用非托管資源的類時,才需要使用System.GC的成員。
第四個問題,構建可終結對象(Finalize()虛方法,使用非托管資源時)這種方式速度慢(因為完成一個終結,至少要進行兩次垃圾回收)
當為自定義的類重寫該方法時,就建立了一個地方,來為類型執行必要的清理邏輯。因為這個成員被定義為保護的,所以不可能通過點操作符從類實力中直接調用一個對象的Finalize()方法。相反,在從內存中刪除這個對象之前,垃圾回收器會調用對象的這個方法。
重寫System.Object.Finalize()方法:
重寫該方法的唯一原因是,C#類通過PInvoke或復雜的COM互操作性任務使用了非托管資源。
始終要記住,Finalize()方法的作用是保證.net對象能在垃圾回收時清除非托管資源。
第五個問題,構建可處置對象。
當垃圾回收生效時,可以利用終結器來釋放非托管資源。然而,因為很多非托管資源都是非常寶貴的,所以他們應該盡可能快的被清除,而不能依靠垃圾回收的發生。除了重寫Finalize方法之外,類還可以實現IDisposable接口,它定義了一個名為Dispose的方法。
第六個問題,延遲對象實例化。
Lazy<>類的用法,從根本上說,這個新的泛型類可以確保昂貴的對象只在用戶需要的時候才會進行分配,這就實現了延遲初始化。
小結:本小節主要介紹對象生命周期和垃圾回收的一些知識,以及最後的延遲對象初始化。從1-6小結,主要都是一些基本的C#編程結構的介紹,接下來,我們將會進行一些高級C#編程結構的陳述和學習。