析构函数:
(来自百度百科)析构函数(destructor
) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new
开辟了一片内存空间,应在退出前在析构函数中用delete
释放)。
C#
中的析构函数定义与C++
类似,~+函数名的方法:
public class FinalizeClass
{
~FinalizeClass()
{
//在这里,清理非托管资源
}
}
生成的IL代码:
.class public auto ansi beforefieldinit Test.FinalizeClass
extends [mscorlib]System.Object
{
// Methods
.method family hidebysig virtual
instance void Finalize () cil managed
{
// Method begins at RVA 0x2070
// Code size 25 (0x19)
.maxstack 1
.try
{
IL_0000: nop
IL_0001: ldstr "FinalizeClass的析构函数"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: nop
IL_000d: leave.s IL_0017
} // end .try
finally
{
IL_000f: ldarg.0
IL_0010: call instance void [mscorlib]System.Object::Finalize()
IL_0015: nop
IL_0016: endfinally
} // end handler
IL_0017: nop
IL_0018: ret
} // end of method FinalizeClass::Finalize
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x20a8
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method FinalizeClass::.ctor
} // 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
模式替代)。