# 2019q1 Homework6 (gc) contributed by < `rebvivi` > ###### tags: `linux2019` * [F10: gc](https://hackmd.io/s/SkZpf5HiE) ## 作業要求 - 完成 第 7 週測驗題 (下) 所要求的 mark & sweep garbage collection 原理解說和程式碼分析 - 改進 ogc 程式,使其得以在多執行緒的環境下正確運作 - 比較其他 GC 實作,提出 ogc 改進計畫並落實 ## mark & sweep garbage collection 原理解說和程式碼分析 >如果呼叫 `gc_init()` 代表使用 garbage collector > >在 `gc_init()` 會建立 `__gc_object` ,為了之後 `gc_alloc()` allocate memory > ```cpp void gc_init(void *ptr, size_t limit) { if (__gc_object.ref_count) { __gc_object.ref_count++; return; } __gc_object = (gc_t){.stack_start = ptr, .ptr_map = {NULL}, .ptr_num = 0, .ref_count = 1, .limit = limit, .min = UINTPTR_MAX, .max = 0, .globals = NULL}; } ``` >當我們不需要使用 garbage collector 時,我們就會呼叫 `gc_destroy()` ```cpp void gc_destroy(void) { __gc_object.ref_count--; if (__gc_object.ref_count <= 0) { __gc_object.ref_count = 0; for (int i = -1; ++i < PTR_MAP_SIZE;) { gc_list_t *m = __gc_object.ptr_map[i]; while (m) { gc_list_t *tmp = m; free((void *) (m->data.start)); m = m->next; free(tmp); } __gc_object.ptr_map[i] = 0; } } } ``` --- >`gc_alloc()` 會 allocate 一個 user 所要求的 memory size > >>在 16 行的地方,會在對應到的 `ptr_map` bucket 中新增 `p`,`p` 裡頭紀錄著我們 malloc 的起始位址、 malloc 的 size 、 一個紀錄是否被 marked 的欄位 >> >>接著在 17 行的地方將 `__gc_object.ptr_num` +1 >> >>第18行,如果 `__gc_object.ptr_num >= __gc_object.limit` ,也就是某個臨界數值,就會觸發 `gc_run()`,開始執行 mark & sweep garbage collection >> ```cpp= void *gc_alloc(size_t size) { uintptr_t ptr; if (!(ptr = (uintptr_t) malloc(size))) return NULL; gc_ptr_t p = (gc_ptr_t){ .start = ptr, .size = size, .marked = true, }; if (__gc_object.min > ptr) __gc_object.min = ptr; if (__gc_object.max < ptr + size) __gc_object.max = ptr + size; gc_list_add(&__gc_object.ptr_map[HASH(ptr) % PTR_MAP_SIZE], p); __gc_object.ptr_num++; if (__gc_object.ptr_num >= __gc_object.limit) gc_run(); return (void *) ptr; } ``` ```cpp void gc_run(void) { gc_mark_stack(); gc_sweep(); } ``` >在 `gc_alloc` 的第 16 行,我們是用 `HASH(ptr) % PTR_MAP_SIZE` 的方式找到對應到的 `ptr_map` bucket > > >==以下是我的推測== >`ptr >> 3` 往右 shift 3 個 bit 是因為取 high-order bit 會讓數字變得更接近 random >,減少 collision,讓整個 hash function 更接近 uniform hash function > > >`HASH(ptr) % PTR_MAP_SIZE` ,總共有編號 0~63 個 bucket ,會 mod `PTR_MAP_SIZE` 是為了分配到對應的 bucket ```cpp #define PTR_MAP_SIZE 64 #define HASH(ptr) ((uintptr_t) ptr >> 3) ``` --- >第 3 行,宣告了一個 `tmp` , `tmp` 所在的 address 會是已經使用過的 address 空間的後面 > >第 4 行使用 `gc_mark()` ,將目前使用的 stack 的 starting address 到 ending address mark 起來 ```cpp= void gc_mark_stack(void) { uint8_t tmp; gc_mark(__gc_object.stack_start, &tmp); for (gc_list_t *e = __gc_object.globals; e; e = e->next) { gc_mark((uint8_t *) (e->data.start), (uint8_t *) (e->data.start + e->data.size)); } } ``` ```cpp void gc_mark(uint8_t *start, uint8_t *end) { if (start > end) swap_ptr(&start, &end); while (start < end) { gc_list_t *idx = gc_ptr_index((uintptr_t)(*((void **) start))); if (idx && idx->data.marked != true) { idx->data.marked = true; gc_mark((uint8_t *) (idx->data.start), (uint8_t *) (idx->data.start + idx->data.size)); } start++; } } ``` --- >`gc_sweep()` 會 free 掉沒有被 marked 的物件的空間 > >第 7 行的 if 中,如果物件沒有被 marked ,就會將它 free 掉 > >第 11 行的 else 中,如果物件有被 marked ,就會將它改成沒有被 marked 的狀態 ```cpp= void gc_sweep(void) { for (int i = 0; ++i < PTR_MAP_SIZE;) { gc_list_t *e = __gc_object.ptr_map[i]; int k = 0; while (e) { if (!e->data.marked) { gc_mfree(e); e = e->next; gc_list_del(&__gc_object.ptr_map[i], k); } else { e->data.marked = false; e = e->next; } k++; } } } ```