由於.NET平台的自動垃圾收集機制,C#語言中類的析構器不再如傳統C++那么必要,析構器不再承擔對象成員的記憶體釋放--自動垃圾收集機制保證記憶體的回收。實際上C#中已根本沒有delete操作!析構器只負責回收處理那些非系統的資源,比較典型的如:打開的檔案,獲取的視窗句柄,資料庫連線,網路連線等等需要用戶自己動手釋放的非記憶體資源。我們看下面例子的輸出:
using System;
class MyClass1
{
~MyClass1()
{
Console.WriteLine("MyClass1's destructor");
}
}
class MyClass2: MyClass1
{
~MyClass2()
{
Console.WriteLine("MyClass2's destructor");
}
}
public class Test
{
public static void Main()
{
MyClass2 MyObject = new MyClass2();
MyObject = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
編譯程式並運行可以得到下面的輸出:
MyClass2's destructor
MyClass1's destructor
其中程式中最後兩句是保證類的析構器得到調用。GC.Collect()是強迫通用語言運行時進行啟動垃圾收集執行緒進行回收工作。而GC.WaitForPendingfinalizers()是掛起目前的執行緒等待整個終止化(Finalizaion)操作的完成。終止化(Finalizaion)操作保證類的析構器被執行,這在下面會詳細說明。
析構器不會被繼承,也就是說類內必須明確的聲明析構器,該類才存在析構器。用戶實現析構器時,編譯器自動添加調用父類的析構器,這在下面的Finalize方法中會詳細說明。析構器由於垃圾收集機制會被在合適的的時候自動調用,用戶不能自己調用析構器。只有實例析構器,而沒有靜態析構器。
那么析構器是怎么被自動調用的?這在 .Net垃圾回收機制由一種稱作終止化(Finalizaion)的操作來支持。.Net系統預設的終止化操作不做任何操作,如果用戶需要釋放非受管資源,用戶只要在析構器內實現這樣的操作即可--這也是C#推薦的做法。我們看下面這段代碼:
using System;
class MyClass1
{
~MyClass1()
{
Console.WritleLine("MyClass1 Destructor");
}
}
而實際上,從生成的中間代碼來看我們可以發現,這些代碼被轉化成了下面的代碼:
using System;
class MyClass1
{
protected override void Finalize()
{
try
{
Console.WritleLine("My Class1 Destructor");
}
finally
{
base.Finalize();
}
}
}
實際上C#編譯器不允許用戶自己重載或調用Finalize方法--編譯器徹底禁止了父類的Finalize方法(由於C#的單根繼承性質,System.Object類是所有類的祖先類,自然每個類都有Finalize方法),好像這樣的方法根本不存在似的。我們看下面的代碼實際上是錯的:
using System;
class MyClass
{
override protected void Finalize() {}// 錯誤
public void MyMethod()
{
this.Finalize();// 錯誤
}
}
但下面的代碼卻是正確的:
using System;
class MyClass
{
public void Finalize()
{
Console.WriteLine("My Class Destructor");
}
}
public class Test
{
public static void Main()
{
MyClass MyObject=new MyClass();
MyObject.Finalize();
}
}
實際上這裡的Finalize方法已經徹底脫離了“終止化操作”的語義,而成為C#語言的一個一般方法了。值得注意的是這也禁止了父類System.Object的Finalize方法,所以要格外小心!
終止化操作在.Net運行時里有很多限制,往往不被推薦實現。當對一個對象實現了終止器(Finalizer)後,運行時便會將這個對象的引用加入一個稱作終止化對象引用集的佇列,作為要求終止化的標誌。當垃圾收集開始時,若一個對象不再被引用但它被加入了終止化對象引用集的佇列,那么運行時並不立即對此對象進行垃圾收集工作,而是將此對象標誌為要求終止化操作對象。待垃圾收集完成後,終止化執行緒便會被運行時喚醒執行終止化操作。顯然這之後要從終止化對象引用集的鍊表中將之刪去。而只有到下一次的垃圾收集時,這個對象才開始真正的垃圾收集,該對象的記憶體資源才被真正回收。容易看出來,終止化操作使垃圾收集進行了兩次,這會給系統帶來不小的額外開銷。終止化是通過啟用執行緒機制來實現的,這有一個執行緒安全的問題。.Net運行時不能保證終止化執行的順序,也就是說如果對象A有一個指向對象B的引用,兩個對象都有終止化操作,但對象A在終止化操作時並不一定有有效的對象A引用。.Net運行時不允許用戶在程式運行中直接調用Finalize()方法。如果用戶迫切需要這樣的操作,可以實現IDisposable接口來提供公共的Dispose()方法。需要說明的是提供了Dispose()方法後,依然需要提供Finalize方法的操作,即實現假託的析構函式。因為Dispose()方法並不能保證被調用。所以.Net運行時不推薦對對象進行終止化操作即提供析構函式,只是在有非受管資源如資料庫的連線,檔案的打開等需要嚴格釋放時,才需要這樣做。
大多數時候,垃圾收集應該交由.Net運行時來控制,但有些時候,可能需要人為地控制一下垃圾回收操作。例如在操作了一次大規模的對象集合後,我們確信不再在這些對象上進行任何的操作了,那我們可以強制垃圾回收立即執行,這通過調用System.GC.Collect() 方法即可實現,但頻繁的收集會顯著地降低系統的性能。還有一種情況,已經將一個對象放到了終止化對象引用集的鏈上了,但如果我們在程式中某些地方已經做了終止化的操作,即明確調用了Dispose()方法,在那之後便可以通過調用System.GC.SupressFinalize()來將對象的引用從終止化對象引用集鏈上摘掉,以忽略終止化操作。終止化操作的系統負擔是很重的。
在深入了解了.NET運行時的自動垃圾收集功能後,我們便會領會C#中的析構器為什麼繞了這么大的彎來實現我們的編程需求,才能把記憶體資源和非記憶體資源的回收做的遊刃有餘--這也正是析構的本原!
相關詞條
-
虛析構函式
虛析構函式是為了解決基類的指針指向派生類對象,並用基類的指針刪除派生類對象。 如果某個類不包含虛函式,那一般是表示它將不作為一個基類來使用。當一個類不準...
虛析構函式 虛析構函式舉例 -
純虛析構函式
在某些類里聲明純虛析構函式很方便。 }; 這個類有一個純虛函式,所以它是抽象的,而且它有一個虛析構函式,所以不會產生析構函式問題。
-
構造器
構造器是Java和C#學習中很重要的一個概念,構造器可以提供許多特殊的方法,構造器作為一種方法,負責類中成員變數(域)的初始化。實例構造器分為預設構造器...
分類 作用 用法 繼承方式 -
彭構雲
彭構雲,諱名雲、字構雲、號廷監,別號夢鯉,行亁一, 學者稱曰:“介亭夫子”。公元715年(唐玄宗開元三年)生,江西省宜春市袁州區人,江西著名的隱士之一,...
人物簡介 人物評價 個人作品 族屬分布 軼事典故 -
中國詩學道器論
中國詩學道器論由安徽教育出版社於2010年出版。
圖書信息 內容簡介 目錄 -
《文心雕龍》
》不計在內),以“剖情析采”為中心,重點研究有關創作過程中各個方面的問題﹐是創作論。《時序》、《才略》、《知音》、《程器》等4篇,則主要是文學史論...然而彩,有心之器,其無文歟?”把詩文的起源提到了自然之道的哲學高度,視為...
簡介 主導思想 集部 作者簡介 文學批評 -
c++0x
C++是具有國際標準的程式語言,通常稱作ANSI/ISO C++,1998年國際標準組織(ISO)頒布了C++語言的國際標準ISO/IEC 1488-1...
成為國際標準 核心語言擴充 核心語言的運行期表現強化 核心語言建構期表現的加強 -
類[編程術語]
函式”。構造函式與析構函式構造函式和析構函式是特殊的成員函式,和普通成員函式不同的地方在於:函式名固定構造函式和析構函式的函式名必須是類名。聲明格式不同構造函式和析構函式沒有返回值,連空返回值——void也沒有。構造...
介紹 用法 特性 示例 -
C++編程思想(兩卷合訂本)
確保初始化 1566.2 用析構函式確保清除 1576.3 清除... 1616.4 帶有構造函式和析構函式的Stash 1626.5 帶有構造函式和析構函式的Stack 1646.6 聚合初始化...