一直是.NET程序员,可是.NET的核心其实仍是C++,因此我准备花 一点时间来研究CoreCLR和CoreFX.但愿这个系列的文章能给你们带来 帮助。程序员
GC的代码有不少不少,并且结构层次对于一个初学者来讲,很难很快或者很慢掌握,因此个人建议是,抓住一段功能,到实际当中去看。原本想从开头 跟你们讲的,可是看 开头也是乱的,反正也没有多少经验,索性随便讲。函数
//返回第二个参数seg的直接前驱节点 heap_segment* heap_segment_prev (heap_segment* begin, heap_segment* seg) { //判断是否begin指针指向了NULL assert (begin != 0); //定义一个局部的变量,让第一个参数begin指向这个局部的指针。 heap_segment* prev = begin; //首先获得以begin为基准的下一个堆片断的地址 heap_segment* current = heap_segment_next (begin); //循环判断“下一个”地址是否是和函数的第二个参数的地址是指向同一块内存 while (current && current != seg) { //把获得的current当前的地址临时指向prev prev = current; //去尝试获得下一个地址 current = heap_segment_next (current); } //上一个循环结束,若是当前segment等于形参,返回上一个 if (current == seg) { return prev; } //若是当前segment是头结点,那么没有直接前驱,返回空 else { return 0; } }
注意这里的heap_segment是类名,定义在gcpriv.h中,heap_segment_prev是方法的名称。heap_segment,个人理解其实很简单,就是 “堆的集合”,集合当中的每一个元素,就是它的一个segment,每一个元素是链式链接的。ui
让咱们来看看这个类的真容吧,这很重要,要注意它们的类型不少 都是Uint8_t,那么 这个结构体有什么特色呢?按照posix标准,通常整形对应的*_t类型为:this
1字节 uint8_t
2字节 uint16_t
4字节 uint32_t
8字节 uint64_tspa
由此咱们能够获得一个很重要的信息,它们存放的片断都是 以一个字节为原子的,为何会用这种方式去存储,这个我也不得而知。3d
class heap_segment { public: uint8_t* allocated; //已经分配的空间 uint8_t* committed; //已经被提交的 uint8_t* reserved; //已经被存储的 uint8_t* used; //已经被使用的 uint8_t* mem; //空间 size_t flags; //标记 PTR_heap_segment next; //下一个堆的片断 uint8_t* plan_allocated; //“将要” 分配的空间 //若是BACKGROUND_GC被定义的话,执行以下代码(后台GC) #ifdef BACKGROUND_GC uint8_t* background_allocated; //后台分配 uint8_t* saved_bg_allocated; //已经保存的后台分配 #endif //BACKGROUND_GC //多个堆(不止是一个堆) #ifdef MULTIPLE_HEAPS gc_heap* heap; //这个类毕竟复杂,之后会专门抽出章节来讲 #endif //MULTIPLE_HEAPS #ifdef _MSC_VER // Disable this warning - we intentionally want __declspec(align()) to insert padding for us #pragma warning(disable:4324) // structure was padded due to __declspec(align()) #endif aligned_plug_and_gap padandplug; #ifdef _MSC_VER #pragma warning(default:4324) // structure was padded due to __declspec(align()) #endif };
其中我来 解释一下PTR_heap_segment,它实际上是一个自定义类型指针
typedef DPTR(class heap_segment) PTR_heap_segment;
下面咱们再回到heap_segment_prev这个方法,若是你能看懂我下面画的这幅图,你就应该能理解这个方法到底要干什么了。其实意图很明显,咱们能够把heap当作是一个链表,暂时咱们 不知道这个链表究竟是什么链表,这个并不重要,重要的是,咱们首先必须知道咱们要从哪一个节点开始,而后要寻找哪个节点,就是分别对应下图的第一个参数和第二个参数,首先咱们会进入一个while循环,若是咱们第一次获得的不为NULL并且获得的heap的下一个节点(segment)不和第二个参数吻合,至于怎么吻合?就是2个地址是否是指指向同一块内存!直到找到为止,返回这个heap指定位置的前置节点。blog
heap_segment_next 这个函数,咱们看到,实际上是指向heap_segment的下一个heap,并做为地址返回。内存
inline PTR_heap_segment & heap_segment_next (heap_segment* inst) { return inst->next; }
下面咱们再来 看一下heap_segment_in_range这个函数。咱们 先看看它的定义。it
inline BOOL heap_segment_in_range_p (heap_segment* inst) { //它返回一个bool类型,固然此BOOL是自定义的 return (!(inst->flags & heap_segment_flags_readonly) || ((inst->flags & heap_segment_flags_inrange) != 0)); }
固然咱们 必须知道一个基本的定义,下面变量是里面自己定义好的。
#define heap_segment_flags_readonly 1 #define heap_segment_flags_inrange 2
由此能够推断,heap_segment_in_range_p为True.
下面咱们来看这个方法,注释我已经打上了,可是我表示怀疑,为何检测边界要经过这种一个一个链式的方式去检测,这样咱们 就要把整个链表跑一次,真的有点怀疑这是否是最好的方法。
//检查堆的边界,即最后一个元素 heap_segment* heap_segment_in_range (heap_segment* ns) { //这里是否会执行,决定于heap_segment的一些特性(我还不知道,因此不乱说) if ((ns == 0) || heap_segment_in_range_p (ns)) { return ns; } else { do { //这段代码实际上是一个循环,它的做用是检测堆的一个“右边”边界 ns = heap_segment_next (ns); } while ((ns != 0) && !heap_segment_in_range_p (ns)); return ns; } }
今天还想写的,不过很晚了,否则明天上班又起不来了。。。。先睡觉,晚安。