析構函數:
(來自百度百科)析構函數(destructor) 與構造函數相反,當對象脫離其作用域時(例如對象所在的函數已調用完畢),系統自動執行析構函數。析構函數往往用來做“清理善后” 的工作(例如在建立對象時用new開辟了一片內存空間,應在退出前在析構函數中用delete釋放)。
C#中的析構函數定義與C++ 類似,~+函數名的方法:
1 public class FinalizeClass 2 { 3 ~FinalizeClass() 4 { 5 //在這里,清理非托管資源 6 } 7 }
生成的IL代碼:
1 .class public auto ansi beforefieldinit Test.FinalizeClass 2 extends [mscorlib]System.Object 3 { 4 // Methods 5 .method family hidebysig virtual 6 instance void Finalize () cil managed 7 { 8 // Method begins at RVA 0x2070 9 // Code size 25 (0x19) 10 .maxstack 1 11 12 .try 13 { 14 IL_0000: nop 15 IL_0001: ldstr "FinalizeClass的析構函數" 16 IL_0006: call void [mscorlib]System.Console::WriteLine(string) 17 IL_000b: nop 18 IL_000c: nop 19 IL_000d: leave.s IL_0017 20 } // end .try 21 finally 22 { 23 IL_000f: ldarg.0 24 IL_0010: call instance void [mscorlib]System.Object::Finalize() 25 IL_0015: nop 26 IL_0016: endfinally 27 } // end handler 28 29 IL_0017: nop 30 IL_0018: ret 31 } // end of method FinalizeClass::Finalize 32 33 .method public hidebysig specialname rtspecialname 34 instance void .ctor () cil managed 35 { 36 // Method begins at RVA 0x20a8 37 // Code size 7 (0x7) 38 .maxstack 8 39 40 IL_0000: ldarg.0 41 IL_0001: call instance void [mscorlib]System.Object::.ctor() 42 IL_0006: ret 43 } // end of method FinalizeClass::.ctor 44 45 } // end of class Test.FinalizeClass
實際上生成了一個Finalize方法,內部調用了Base.Finalize()方法,也就是Object的Finalize 方法。
Finalize方法只能由GC調用,我們是不能調用的。下面說下GC調用Finalize的流程!
Finalization List(Queue)(終結列表)
我們new 一個對象,如果這個對象包含Finalize方法,開辟內存后,指向它的指針會被存放到終結列表中(Object對象除外)。
Freachable Queue (F-reachable終結可到達隊列)
垃圾回收開始時,被判定為垃圾(不可達)的對象如果同時存在於Finalization List中,就會將該對象的指針從Finalization List移除,並存入Freachable Queue中。同時這些對象都變為可達(reachable),不會被GC回收,這樣就意味着這些對象提升到另一代,這里假設為2代對象。
該隊列中的對象都是可達的,並需要執行Finalize方法。執行Finalize方法是由一個高優先級的CLR線程進行的,執行完畢后,會將對象的指針從Freachable Queue中移除(當該隊列為空時,此線程將睡眠,在不為空時被喚醒)。
當再次進行垃圾回收時,原Freachable Queue中的對象經過處理都變為不可達對象(2代),只有當2代內存不足時才會對2代對象進行垃圾回收,這些對象內存才會真正釋放掉。因此含有Finalize方法的對象最少要經過兩次垃圾回收才會被真正釋放。
看圖解:

對象2、3、5、6、10包含Finalize方法,2、5、7、9為不可達對象(GC的目標)。

進行GC時,由於2、5對象包含Finalize方法,因此被放入Freachable Queue中,變為可達對象並提升代,不進行垃圾回收。而對象7、9直接被回收。

如果原Freachable所在代進行GC,就會回收對象2、5的內存。
結論
1.含有Finalize方法的對象最少要經過兩次垃圾回收才會被真正釋放。
2.如非必要,不建議定義Finalize方法(用Dispose模式替代)。
