C++随笔:.NET CoreCLR之GC探索(2)

  首先谢谢 @dudu 和 @张善友 这2位大神能订阅我,本来在写这个系列以前,我一直对写一些核心而且底层的知识持怀疑态度,我为什么持怀疑态度呢?因为一般写高层语言的人99%都不会碰底层,其实说句实话,我以前也不看这些东西,只是因为自己觉得对C++感兴趣,索性乱写点东西,如果有写得不好的地方,还请上面2位大神指出。

  其实我现在虽然写的是C++,但是我打算在后面把C++和.NET的一些基础类库融合起来,我发现写CLR的文章特别少,不知道什么原因。反正,废话不多,开始今天的写作吧,今天依然是把重点集中在GC上面。

  首先跟大家分享一下我这次碰到的一个有趣的东西,就是下面的东西,下面的东西我第一次看,不过我可以确定的是,GC的运行模式有以下。以下都有注释,我就不详解了,不过跟大家说一下我的一个小小的发现:我猜测,以下代码只在64位GC下运行,这句话听起来很拗口,但是我是怎么得到这个结论的呢?

// !!!!!!!!!!!!!!!!!!!!!!!
// make sure you change the def in bcl\system\gc.cs
// if you change this!
//上面那句ENGLISH的意思就是如果要改这里面的值,必须先改变bc1\system\gc.cs里面的定义
//收集器运行模式
enum collection_mode
{
collection_non_blocking = 0x00000001, //非锁定模式
collection_blocking = 0x00000002, //锁定模式
collection_optimized = 0x00000004, //优化模式
collection_compacting = 0x00000008 //最小适配模式 #ifdef STRESS_HEAP //如果定义了Stress_heap,我这里翻译为压力堆,也就是“入堆”
, collection_gcstress = 0x80000000 //这里我觉得应该是GC可以入堆的一个条件吧,虽然我不懂这个是什么意思
#endif // STRESS_HEAP
};

  C++随笔:.NET CoreCLR之GC探索(2)

  虽然我不敢完全说我是对的,但是有一点可以确定,就是它会生成x64,以windows内核为寄托的一个以i结尾的文件,我们可以把它理解为一个临时文件,而GC和windows内存的关系就像一个寄宿关系一样,GC是依赖于windows内核的某个东西生存(全是个人的猜测)。

  那么,这些enmu类型的固定值,在GC当中,到底起了一个什么样的作用呢?我随便找了几个变量,因为这些固定值,其实在GC当中,你可以理解它是无处不在的,就相当于平常写代码的if else 一样普通,大家知道这些变量的用处就行了,不必过于纠结代码是什么意思,因为以后我会带大家慢慢研究。

//如果定义成了后台GC
#ifdef BACKGROUND_GC
if (recursive_gc_sync::background_running_p())
{
//如果mode(暂时可以理解为一个变量)
//collection_non_blocking 值是0x00000001,也就是说mode&collection_non_blocking其实只要比较最右边一位就OK了,也就是mode=1 or 4
if ((mode == collection_optimized) || (mode & collection_non_blocking))
{
return S_OK;
}
if (mode & collection_blocking)
{
pGenGCHeap->background_gc_wait();
if (mode & collection_optimized)
{
return S_OK;
}
}
}

  今天我也不想过多的浪费时间,我打算挑一个有代表性的代码片段来带大家了解GC,所谓GC,GC是什么,那么它肯定有一个Collect的方法;好,那我们就拿这个方法开刀吧。首先我们必须知道几个知识点,对于非c++程序员来说,也还是需要学一点东西,size_t的解释,在这里简单一点说就是long unsigned  int ,我为什么特意把long标红呢?因为我们的GC使用的方法,是在64位环境下运行的,如果是32位的,那么就是unsigned int .

  因为这段代码很长,所以我打算:先讲一部分,然后再讲剩下的部分,不然大家就算看注释,也会眼花的。

//函数返回值。如果这个函数是执行完返回的话将包含具有实际意义的数据,如果立即返回则包含状态信息--发送成功与否 -来自百度
HRESULT //generation(代,比如你和你父母是2代人)
//
GCHeap::GarbageCollect (int generation, BOOL low_memory_p, int mode)
{
//如果定义了64位的。。。也就是说,此GC的CLR是在64位运行时上跑的
#if defined(BIT64) //如果定义了弱存储指针 PS:我也不知道怎么翻译,索性把low翻译成弱。
if (low_memory_p)
{
//1.初始化:所有已经分配的空间
size_t total_allocated = 0;
//所有“想要”分配的空间
size_t total_desired = 0; //如果定义了复杂堆(我自己翻译的)
#ifdef MULTIPLE_HEAPS
int hn = 0; //n_heaps这个定义我没找到,不过我可以确定的是,这个肯定是和MULTIPLE_HEAPS有关的
//大家可以把这个理解为MULTIPLE_HEAPS.Length
for (hn = 0; hn < gc_heap::n_heaps; hn++)
{
//首先是定义一个“临时”的指针hp,让gc堆当中的每一项都指向这个变量。
gc_heap* hp = gc_heap::g_heaps [hn]; //把每一块想要分配的数量,叠加起来。
total_desired += dd_desired_allocation (hp->dynamic_data_of (0)); //总分配量=想要分配的-新分配的
total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))-
dd_new_allocation (hp->dynamic_data_of (0));
}

  大家需要从上面的代码当中,掌握如下几个基础知识点:

  一个是dynamic_data_of方法,它返回了一个dynamic_data类的指针变量,那我们乘胜追击,看看这个方法和类。

    PER_HEAP
dynamic_data* dynamic_data_of (int gen_number);

  首先是方法:

//返回dynamic_data_table的第一个Table,大家可以类比.net中的DataSet
inline
dynamic_data* gc_heap::dynamic_data_of (int gen_number)
{
return &dynamic_data_table [ gen_number ];
}

  然后我们再来看看这个dynamic_data_table,

    PER_HEAP
dynamic_data dynamic_data_table [NUMBERGENERATIONS+1];

  下面来看这张图,突然我恍然大悟,所谓的DataTable,就是我们每次使用的DataSet,其实底层是放在堆栈上面的,以复杂堆的方式存放,而每个DataTable,其实就是堆栈当中的小小的组成而已,不止我理解得对不对。

C++随笔:.NET CoreCLR之GC探索(2)

其中上面代码中的下面2个方法,都是和dynamic_data有关系的。其中ptrdiff_t类型变量通常用来保存两个指针减法操作的结果,这也是需要.NET程序员注意的,毕竟搞.NET是不和指针打交道的。

inline
size_t& dd_desired_allocation (dynamic_data* inst)
{
return inst->desired_allocation;
}
inline
ptrdiff_t& dd_new_allocation (dynamic_data* inst)
{
return inst->new_allocation;
}

  大家还要注意一下小细节,就是+=其实是先+=,再减去新分配的空间。

C++随笔:.NET CoreCLR之GC探索(2)

  今天就说这么多了,因为我还要做其他的事情,最后跟大家卖个关子,那些说.NET没技术含量的人,其实是不了解.NET原理的人。下面的就是一个比较复杂的类,这里面定义了超多的友元类,也就是说,GC表面上是一段代码,其实这就是一部重型航空母舰,需要我们去探索它的结构。

  爱因斯坦曾经说过一句名言:追求真理比占有真理更可贵。我们要做的,不仅仅是一颗螺丝钉。

C++随笔:.NET CoreCLR之GC探索(2)

上一篇:Docker学习笔记_04 Rancher的部署安装(编排选用K8S)


下一篇:Linux学习笔记:常用命令grep、iconv、cp、mv、rm