比如说,有一个类,包含了析构函数
class A
{
public A()
{
Console.WriteLine("Create A Class");
}
~A()
{
Console.WriteLine("Kill A Class");
}
}
当我们实例化这个类的时候
A a = new A()
CLR在分配a这个实例的时候,会检测它是否包含了析构函数~A。
CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
#define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do { \
if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size))) \
{ \
STRESS_LOG_OOM_STACK(_size); \
return NULL; \
} \
} while (false)
注意看这个宏的if 判断语句,它首先是个do-while循环,这个很有意思,wihile包含的条件就是个false,表示它只循环一次。其次会判断_object也就是传递进来的参数newAlloc。是否为NULL,如果是直接返回NULL,因为一个NULL就没必要进行后续动作了。
当它不等于NULL,后续需要_register和 !REGISTER_FOR_FINALIZATION(_object, _size))这两个条件。_register实际上就是判断当前的实例化分配的类A是否包含析构函数操作为:flags & GC_ALLOC_FINALIZE。
而!REGISTER_FOR_FINALIZATION(_object, _size))是重点,它包含了如何把析构函数的对象添加到析构列表。
实际上因为 ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size))这两个是&& ,所以_register就算是包含了析构函数,但是REGISTER_FOR+FINALIZATION返回True。它还是不进入If语句里面去。
重点关注REGISTER_FOR_FINALIZATION
#define REGISTER_FOR_FINALIZATION(_object, _size) \
hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))
它实际上也是个宏,调用了RegisterForFinalization 函数,这个finalize_queue是在hp(hp 就是gc_heap)初始化的时候被Init的。
finallize_queue实例化代码:
bool CFinalize::Initialize()
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
} CONTRACTL_END;
m_Array = new (nothrow)(Object*[100]);
if (!m_Array)
{
ASSERT (m_Array);
STRESS_LOG_OOM_STACK(sizeof(Object*[100]));
if (GCConfig::GetBreakOnOOM())
{
GCToOSInterface::DebugBreak();
}
return false;
}
m_EndArray = &m_Array[100];
for (int i =0; i < FreeList; i++)
{
SegQueueLimit (i) = m_Array;
}
m_PromotedCount = 0;
lock = -1;
#ifdef _DEBUG
lockowner_threadid.Clear();
#endif // _DEBUG
return true;
}
实际上就做了一件事情,就是让SegQueueLimit的每一个元素指向析构列表
我们来看RegisterForFinalization
CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
} CONTRACTL_END;
EnterFinalizeLock();
// Adjust gen
unsigned int dest = gen_segment (gen);
// Adjust boundary for segments so that GC will keep objects alive.
Object*** s_i = &SegQueue (FreeList);
if ((*s_i) == m_EndArray)
{
if (!GrowArray())
{
LeaveFinalizeLock();
if (method_table(obj) == NULL)
{
// If the object is uninitialized, a valid size should have been passed.
assert (size >= Align (min_obj_size));
dprintf (3, (ThreadStressLog::gcMakeUnusedArrayMsg(), (size_t)obj, (size_t)(obj+size)));
((CObjectHeader*)obj)->SetFree(size);
}
STRESS_LOG_OOM_STACK(0);
if (GCConfig::GetBreakOnOOM())
{
GCToOSInterface::DebugBreak();
}
return false;
}
}
Object*** end_si = &SegQueueLimit (dest);
do
{
//is the segment empty?
if (!(*s_i == *(s_i-1)))
{
//no, swap the end elements.
*(*s_i) = *(*(s_i-1));
}
//increment the fill pointer
(*s_i)++;
//go to the next segment.
s_i--;
} while (s_i > end_si);
// We have reached the destination segment
// store the object
**s_i = obj;
// increment the fill pointer
(*s_i)++;
LeaveFinalizeLock();
return true;
}
这个函数做的主要功能
-
获取到当前传递进来的代在m_FillPointers数组的索引4(因为传递进来的代只能是0,所以total_generation_count - gen - 1=4,调用unsigned int dest = gen_segment (gen);)
-
获取到m_FillPointers的最末尾元素的地址Object*** s_i = &SegQueue (FreeList),FreeList=7
-
获取m_FillPointers索引为N的元素地址 Object*** end_si = &SegQueueLimit (dest)
-
判断前一个m_FillPointers的元素是否与当前元素相等。
-
m_FillPointers元素的值取值,实际上就是指向Object的指针。然后++,实际上就是m_array+8.指向析构队列的下一个元素。
-
如此往复循环,找到当前析构对象需要存放的位置。
当GC的时候
- 先判断对象是否存在
- 执行析构对象里面的方法
- 回收掉它