【C#】GC和析構函數(Finalize 方法)


析構函數:

(來自百度百科)析構函數(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模式替代)。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM