CLR析构列表是如何添加析构函数类的

比如说,有一个类,包含了析构函数

1
2
3
4
5
6
7
8
9
10
11
typescript复制代码  class A
{
public A()
{
Console.WriteLine("Create A Class");
}
~A()
{
Console.WriteLine("Kill A Class");
}
}

当我们实例化这个类的时候

1
css复制代码    A a = new A()

CLR在分配a这个实例的时候,会检测它是否包含了析构函数~A。

1
2
3
4
5
6
7
8
9
10
scss复制代码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

1
2
scss复制代码#define REGISTER_FOR_FINALIZATION(_object, _size) \
hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))

它实际上也是个宏,调用了RegisterForFinalization 函数,这个finalize_queue是在hp(hp 就是gc_heap)初始化的时候被Init的。

finallize_queue实例化代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
ini复制代码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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
scss复制代码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;
}

这个函数做的主要功能

  1. 获取到当前传递进来的代在m_FillPointers数组的索引4(因为传递进来的代只能是0,所以total_generation_count - gen - 1=4,调用unsigned int dest = gen_segment (gen);)
  2. 获取到m_FillPointers的最末尾元素的地址Object*** s_i = &SegQueue (FreeList),FreeList=7
  3. 获取m_FillPointers索引为N的元素地址 Object*** end_si = &SegQueueLimit (dest)
  4. 判断前一个m_FillPointers的元素是否与当前元素相等。
  5. m_FillPointers元素的值取值,实际上就是指向Object的指针。然后++,实际上就是m_array+8.指向析构队列的下一个元素。
  6. 如此往复循环,找到当前析构对象需要存放的位置。

当GC的时候

  1. 先判断对象是否存在
  2. 执行析构对象里面的方法
  3. 回收掉它

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%