一直是.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; } }
今天還想寫的,不過很晚了,否則明天上班又起不來了。。。。先睡覺,晚安。