比如说,有一个类,包含了析构函数
1 | typescript复制代码 class A |
当我们实例化这个类的时候
1 | css复制代码 A a = new A() |
CLR在分配a这个实例的时候,会检测它是否包含了析构函数~A。
1 | scss复制代码CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE); |
注意看这个宏的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 | scss复制代码#define REGISTER_FOR_FINALIZATION(_object, _size) \ |
它实际上也是个宏,调用了RegisterForFinalization 函数,这个finalize_queue是在hp(hp 就是gc_heap)初始化的时候被Init的。
finallize_queue实例化代码:
1 | ini复制代码bool CFinalize::Initialize() |
实际上就做了一件事情,就是让SegQueueLimit的每一个元素指向析构列表
我们来看RegisterForFinalization
1 | scss复制代码CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size) |
这个函数做的主要功能
- 获取到当前传递进来的代在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的时候
- 先判断对象是否存在
- 执行析构对象里面的方法
- 回收掉它
本文转载自: 掘金