Net 6 LTS CLR GCRoot是如何遍历所有的引用Object的对象

219 阅读2分钟

                                  关注公众号,获取源码和学习文件

                                      

要了解这个问题,首先要知道。根对象实际上是通过GCInfo里面的存储位数获得的,而GCInfo 是RyuJit在编译IL代码为机器码的时候生成的。

首先就是获取到根对象,然后把根对象的所有引用的对象给它找出来呢

看一段代码:

#define go_through_object(mt,o,size,parm,start,start_useful,limit,exp)      \
{                                                                           \
    CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt));           \
    CGCDescSeries* cur = map->GetHighestSeries();                           \
    ptrdiff_t cnt = (ptrdiff_t) map->GetNumSeries();                        \
                                                                            \
    if (cnt >= 0)                                                           \
    {                                                                       \
        CGCDescSeries* last = map->GetLowestSeries();                       \
        uint8_t** parm = 0;                                                 \
        do                                                                  \
        {                                                                   \
            assert (parm <= (uint8_t**)((o) + cur->GetSeriesOffset()));     \
            parm = (uint8_t**)((o) + cur->GetSeriesOffset());               \
            uint8_t** ppstop =                                              \
                (uint8_t**)((uint8_t*)parm + cur->GetSeriesSize() + (size));\
            if (!start_useful || (uint8_t*)ppstop > (start))                \
            {                                                               \
                if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);\
                while (parm < ppstop)                                       \
                {                                                           \
                   {exp}                                                    \
                   parm++;                                                  \
                }                                                           \
            }                                                               \
            cur--;                                                          \
                                                                            \
        } while (cur >= last);                                              \
    }             
    }

go_through_object

就是获取到根对象所引用的所有object 然后把它给遍历出来。

这个遍历过程是怎么样的呢?

代码说话

首先它,通过MethodTable实例化一个GCDESC。这个结构体里面不包含任何字段,只是函数操作。下面来了。

GetHighestSeries():这个函数把methodtable的地址前移24字节。为啥是24因为它首先把PTR_size_t实际上就是先挪8字节。然后再把PTR_CGCDescSeries减去1,因为PTR_CGCDescSeries实际上包含了两个size_t ,那么实际上就是16字节。8+16刚好24字节。

GetNumSeries():这个函数是把methodtable地址值前挪8位。然后获取它里面的值。这个里面的值表示当前根对象引用了几个对象。也就是引用对象的个数。

GetNumSeries(): 这个函数是把objectheader的指针加上objheader的长度,以便获取到methodtable的地址。也就是objecft对象的地址。

在cur >= last之前,会一直遍历所引用的对象,把它完全标记为止。如果没有标记完,会一直do while循环查找。

查找一个跟对象的步骤实际上就这么多。但是呢,还得验证下,口说无凭嘛。

using System;

namespace KongZhiTai
{
    public class Ceshi
    {
        public int Id;
        public Ceshi Prev;
        public Ceshi Next;

        public Ceshi(int id)
        {
            Id = id;
        }

        public void SetNetxt(Ceshi ceshi)
        {
            ceshi.Prev = this;
            ceshi.Next = ceshi;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Ceshi ceshi = new Ceshi(3333);
            ceshi.SetNetxt(ceshi);
            Console.WriteLine(ceshi);
        }
    }
}

看上面的C#代码。观察CeShi类的实例ceshi。

Net 6 LTS CLR GCRoot是如何遍历所有的引用Object的对象