├── .gitignore ├── LICENSE ├── README.md ├── auto_test.sh ├── compact-lisp2 ├── Makefile ├── README.md ├── compact.c └── test.c ├── compact-two-finger ├── Makefile ├── README.md ├── compact.c └── test.c ├── conservative.md ├── copying-or-mark ├── Makefile ├── README.md ├── copying.c ├── header.h ├── mark-sweep.c └── test.c ├── copying ├── Makefile ├── README.md ├── copying.c └── test.c ├── gc-golang ├── CMakeLists.txt ├── Makefile ├── README.md ├── atomic │ ├── CMakeLists.txt │ ├── atomic.c │ └── atomic.h ├── cache │ ├── CMakeLists.txt │ ├── README.md │ ├── cache.c │ └── cache.h ├── central │ ├── CMakeLists.txt │ ├── README.md │ ├── central.c │ └── central.h ├── heap │ ├── CMakeLists.txt │ ├── README.md │ ├── arena.c │ ├── arena.h │ ├── heap.c │ ├── heap.h │ ├── heap_common.c │ ├── treap.c │ └── treap.h ├── malloc │ ├── CMakeLists.txt │ ├── fixalloc.c │ ├── fixalloc.h │ ├── lineralloc.c │ ├── lineralloc.h │ ├── malloc.c │ ├── malloc.h │ └── mallocgc.c ├── mgc │ ├── CMakeLists.txt │ ├── README.md │ ├── bitarena.c │ ├── bitarena.h │ ├── bitmap.c │ ├── bitmap.h │ ├── drain.c │ ├── gc.c │ ├── gc.h │ ├── gc_sweepbuf.c │ ├── gc_sweepbuf.h │ ├── gc_work.c │ ├── gc_work.h │ ├── mark.c │ ├── mark.h │ ├── root.h │ ├── root.s │ ├── scanstack.c │ └── sweep.c ├── proc │ ├── CMakeLists.txt │ ├── README.md │ ├── proc.c │ └── proc.h ├── readme ├── span │ ├── CMakeLists.txt │ ├── README.md │ ├── span.c │ ├── span.h │ └── spanclass.c ├── sys │ ├── CMakeLists.txt │ ├── array.c │ ├── array.h │ ├── defines.h │ ├── gc.h │ ├── gpm.c │ ├── gpm.h │ ├── mem_linux.c │ ├── platform.h │ ├── sys.c │ └── sysapi.h └── test.c ├── gc-python ├── Array.c ├── Array.h ├── Hugmem.c ├── Hugmem.h ├── Makefile ├── README.md ├── gc.c ├── gc.h ├── gc_test.c ├── help.h ├── malloc │ ├── Makefile │ ├── obmalloc.c │ ├── obmalloc.h │ ├── python.h │ └── test.c ├── root.h ├── root.s └── sweep.c ├── gc-try ├── Makefile ├── README.md ├── mark_sweep.c ├── root.h ├── root.s └── test.c ├── gc.c ├── gc.h ├── generational ├── Makefile ├── README.md ├── generational.c ├── generational.h ├── mark_sweep.c └── test.c ├── heap.md ├── mark-sweep-bitmap ├── Makefile ├── README.md ├── gc.c ├── gc.h ├── mark_sweep.c └── test.c ├── mark-sweep-multi-free-list ├── Makefile ├── README.md ├── gc.c ├── gc.h ├── mark_sweep.c └── test.c ├── mark-sweep ├── Makefile ├── README.md ├── mark_sweep.c ├── run.sh └── test.c ├── reame ├── refcount ├── Makefile ├── README.md ├── refcount.c ├── refcount.h └── test.c ├── root.md └── tri-color-marking ├── Makefile ├── README.md ├── stack.c ├── stack.h ├── test.c ├── tri-color.c └── tri-color.h /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | gc 3 | *.dSYM 4 | bin 5 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 brewlin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gc-learning 2 |

3 | GitHub 4 | GitHub code size in bytes 5 |

6 | 7 | 垃圾回收算法的分析与实现,基于c实现各系列gc [docs:分析文档](https://wiki.brewlin.com/wiki/algorithm/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) 8 | 9 | 在编程语言中引入gc的例子:[tu-lang](https://github.com/tu-lang/tu) 10 | 11 | ```asciidoc 12 | > dos2unix auto_test.sh 13 | > sh auto_test.sh 14 | ``` 15 | ## @demo 16 | ```c 17 | #include "gc.h" 18 | 19 | Obj* p = gc_malloc(sizeof(Obj)); 20 | // don't need care the `free`,let it go 21 | 22 | ``` 23 | 24 | ## @analysis 25 | - [x] python的gc实现 26 | - [x] golang的gc实现 27 | - [x] 内存分配 28 | - [x] 标记+清除 29 | - [ ] 并发gc实现 30 | - [ ] 实现一个可生产应用的gc 31 | 32 | 33 | ## @mark-sweep 34 | - [x] 标记清除算法-基础实现 35 | - [x] 标记清除算法-多链表法实现 36 | - [x] 标记清除算法-位图式 37 | 38 | ## @reference-count 39 | - [x] 引用计数算法 40 | 41 | ## @copying 42 | - [x] 复制算法-基础实现 43 | - [x] 复制算法+标记清除-组合应用实现 44 | 45 | ## @compact 46 | - [x] 压缩算法-lisp2算法 47 | - [x] 压缩算法-two_finger 48 | 49 | ## @generational 50 | - [x] 分代算法-复制+标记清除 51 | 52 | ## @incremental 53 | - [x] 增量式算法-三色标记 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /auto_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | assert(){ 3 | input="$1" 4 | cd "$input" 5 | make 6 | ./gc 7 | if [ "$?" != 0 ]; then 8 | echo "\033[31m test $input failed code:$?\033[0m" 9 | exit 1 10 | fi 11 | cd .. 12 | } 13 | read_dir(){ 14 | for file in `ls $1` 15 | do 16 | if [ -d $1"/"$file ] ; then 17 | assert $1"/"$file 18 | echo "\033[32m [compile] $file pass! \033[0m \n" 19 | fi 20 | done 21 | } 22 | read_dir ./ 23 | echo 24 | echo "\033[32m all passing..... \033[0m" 25 | -------------------------------------------------------------------------------- /compact-lisp2/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) ../gc.c -I ../ 13 | 14 | -------------------------------------------------------------------------------- /compact-lisp2/README.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /compact-lisp2/compact.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | /** 3 | * 初始化一个堆,关闭自动扩充heap,方便测试 4 | **/ 5 | void gc_init(size_t heap_size) 6 | { 7 | //关闭自动扩充堆 8 | auto_grow = 0; 9 | 10 | gc_heaps_used = 1; 11 | //使用sbrk 向操作系统申请大内存块 12 | void* p = sbrk(heap_size + PTRSIZE); 13 | gc_heaps[0].slot = (Header *)ALIGN((size_t)p, PTRSIZE); 14 | gc_heaps[0].size = heap_size; 15 | gc_heaps[0].slot->size = heap_size; 16 | gc_heaps[0].slot->next_free = NULL; 17 | //将堆初始化到free_list 链表上 18 | gc_free(gc_heaps[0].slot + 1); 19 | } 20 | /** 21 | * 开始进行gc标记 22 | **/ 23 | void* gc_mark(void *ptr){ 24 | GC_Heap *gh; 25 | Header *hdr; 26 | 27 | /* 校验指针 */ 28 | if (!(gh = is_pointer_to_heap(ptr))){ 29 | // printf("not pointer\n"); 30 | return ptr; 31 | } 32 | if (!(hdr = get_header(gh,ptr))) { 33 | // printf("not find header\n"); 34 | return ptr; 35 | } 36 | //校验对象合法 37 | if (!FL_TEST(hdr, FL_ALLOC)) { 38 | printf("flag not set alloc\n"); 39 | return ptr; 40 | } 41 | //校验是否已经被标记过 42 | if (FL_TEST(hdr, FL_MARK)) { 43 | //printf("flag not set mark\n"); 44 | return ptr; 45 | } 46 | 47 | /* 开始标记 hdr->flag */ 48 | FL_SET(hdr, FL_MARK); 49 | // printf("mark ptr : %p, header : %p\n", ptr, hdr); 50 | //进行子节点递归 标记 51 | for (void* p = ptr; p < (void*)NEXT_HEADER(hdr); p++) { 52 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 53 | //递归进行 引用的拷贝 54 | gc_mark(*(void **)p); 55 | } 56 | return ptr; 57 | } 58 | /** 59 | * 计算对象需要移动的目的地址 60 | * src: obj 61 | * dest: obj->forwarding 62 | */ 63 | void set_forwarding_ptr(void) 64 | { 65 | size_t i; 66 | Header *p, *pend, *pnext ,*new_obj; 67 | 68 | //当前算法只有一个堆 69 | for (i = 0; i < gc_heaps_used; i++) 70 | { 71 | pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size); 72 | p = gc_heaps[i].slot; 73 | new_obj = gc_heaps[i].slot; 74 | //堆的起始为止 因为堆的内存可能被分成了很多份,所以需要遍历该堆的内存 75 | for (; p < pend; p = NEXT_HEADER(p)) 76 | { 77 | //查看该堆是否已经被使用 78 | if (FL_TEST(p, FL_ALLOC)) { 79 | //查看该堆是否被标记过 80 | if (FL_TEST(p, FL_MARK)) { 81 | p->forwarding = new_obj; 82 | //new_obj 继续下移 p个空间大小 83 | new_obj = (void*)new_obj + p->size; 84 | } 85 | } 86 | } 87 | } 88 | } 89 | /** 90 | * 更新子类指针的引用 91 | */ 92 | void adjust_ptr() 93 | { 94 | //遍历所有对象 95 | for(int i = 0; i < root_used; i ++){ 96 | Header* forwarding = CURRENT_HEADER(roots[i].ptr)->forwarding; 97 | roots[i].ptr = forwarding+1; 98 | *(Header**)roots[i].optr = forwarding+1; 99 | } 100 | //下面更新引用 101 | size_t i; 102 | Header *p, *pend, *pnext ,*new_obj; 103 | 104 | //当前只有一个堆 105 | for (i = 0; i < gc_heaps_used; i++) 106 | { 107 | //pend 堆内存结束为止 108 | pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size); 109 | 110 | //堆的起始为止 因为堆的内存可能被分成了很多份,所以需要遍历该堆的内存 111 | for (p = gc_heaps[i].slot; p < pend; p = NEXT_HEADER(p)) 112 | { 113 | //可能申请的内存 里面又包含了其他内存 114 | for (void* obj = p+1; obj < (void*)NEXT_HEADER(p); obj++) 115 | { 116 | GC_Heap *gh; 117 | Header *hdr; 118 | //正确找到了 child 引用 119 | if (!(gh = is_pointer_to_heap(*(void**)obj))) continue; 120 | if((hdr = get_header(gh,*(void**)obj))) { 121 | *(Header **) obj = hdr->forwarding + 1; //更新引用 122 | } 123 | } 124 | } 125 | } 126 | 127 | } 128 | /** 129 | * 前面两个步骤: 130 | * 计算好了移动后的地址 131 | * 更新好了子类的引用地址 132 | * 现在就开始实际的移动拷贝对象即可 133 | */ 134 | void move_obj() 135 | { 136 | size_t total; 137 | Header *p, *pend, *pnext ,*new_obj,*free_p; 138 | 139 | //只有一个堆 140 | for (size_t i = 0; i < gc_heaps_used; i++) { 141 | //pend 堆内存结束为止 142 | pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size); 143 | 144 | free_p = gc_heaps[i].slot; 145 | total = gc_heaps[i].size; 146 | //堆的起始为止 因为堆的内存可能被分成了很多份,所以需要遍历该堆的内存 147 | for (p = gc_heaps[i].slot; p < pend; p = NEXT_HEADER(p)) 148 | { 149 | //查看该堆是否已经被使用 150 | if (FL_TEST(p, FL_ALLOC)) { 151 | //查看该堆是否被标记过 152 | if (FL_TEST(p, FL_MARK)) { 153 | new_obj = p->forwarding; 154 | //将当前的对象拷贝到之前计算好的位置 155 | memcpy(new_obj, p, p->size); 156 | FL_UNSET(new_obj,FL_MARK); 157 | //空闲链表下移 158 | free_p = (void*)free_p + new_obj->size; 159 | total -= new_obj->size; 160 | 161 | } 162 | } 163 | } 164 | } 165 | //这个时候free_p 后面就是空闲列表了 166 | free_list = free_p; 167 | //total 需要单独计算剩余空间 168 | free_list->size = total; 169 | free_list->next_free = NULL; 170 | //方便测试 把空闲空间都清空 171 | memset(free_list + 1,0,total); 172 | 173 | } 174 | /** 175 | * 执行gc 176 | */ 177 | void gc(void) 178 | { 179 | printf("start gc()\n"); 180 | //gc 递归标记 181 | for(int i = 0;i < root_used;i++) 182 | gc_mark(roots[i].ptr); 183 | 184 | //设置forwarding指针 计算好移动后的位置 185 | set_forwarding_ptr(); 186 | //更新子类引用 187 | adjust_ptr(); 188 | //移动对象 189 | move_obj(); 190 | } 191 | 192 | -------------------------------------------------------------------------------- /compact-lisp2/test.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | 3 | typedef struct t{ 4 | int value; 5 | struct t* next; 6 | }T; 7 | int clear(){ 8 | 9 | free_list = NULL; 10 | root_used = 0; 11 | } 12 | void test_auto_gc(){ 13 | //初始化3个T空间大小 14 | gc_init(3 * (sizeof(T) + HEADER_SIZE)); 15 | 16 | T* t1 = gc_malloc(sizeof(T)); 17 | assert(t1); 18 | T* t2 = gc_malloc(sizeof(T)); 19 | assert(t2); 20 | T* t3 = gc_malloc(sizeof(T)); 21 | assert(t3); 22 | 23 | //内存不够了 会自动gc 24 | T* t4 = gc_malloc(sizeof(T)); 25 | assert(t4); 26 | 27 | } 28 | /** 29 | * 测试加入root后的gc情况 30 | */ 31 | void test_add_root_gc(){ 32 | //初始化3个T空间大小 33 | gc_init(3 * (sizeof(T) + HEADER_SIZE)); 34 | 35 | T* t1 = gc_malloc(sizeof(T)); 36 | t1->value = 11; 37 | assert(t1); 38 | add_roots(&t1); 39 | T* t2 = gc_malloc(sizeof(T)); 40 | t2->value = 12; 41 | T* t3 = gc_malloc(sizeof(T)); 42 | t1->next = t2; 43 | t2->next = t3; 44 | t3->value = 13; 45 | 46 | //内存不够了 会自动gc 但是gc后依然不够用 47 | T* t4 = gc_malloc(sizeof(T)); 48 | assert(t4 == NULL); 49 | assert(t1->value == 11); 50 | assert(t2->value == 12); 51 | assert(t3->value == 13); 52 | 53 | //将t3去除 54 | t2->next = NULL; 55 | //空间不够 会自动gc 释放 t3 56 | T* t5 = gc_malloc(sizeof(T)); 57 | assert(t1->value == 11); 58 | assert(t2->value == 12); 59 | assert(t3->value == 0); 60 | assert(t5); 61 | } 62 | void main(){ 63 | // 测试不加入 root的时候 会自动进行gc复制 64 | //看看gc复制的结果是否符合预期 65 | test_auto_gc(); 66 | clear(); 67 | 68 | test_add_root_gc(); 69 | } -------------------------------------------------------------------------------- /compact-two-finger/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) ../gc.c -I ../ 13 | 14 | -------------------------------------------------------------------------------- /compact-two-finger/README.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /compact-two-finger/compact.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | 3 | /** 4 | * 压缩算法中方便测试算法本身,只允许一个堆且不允许自动个扩充 5 | **/ 6 | void gc_init(size_t heap_size) 7 | { 8 | //关闭自动扩充堆 9 | auto_grow = 0; 10 | gc_heaps_used = 1; 11 | 12 | //使用sbrk 向操作系统申请大内存块 13 | void* p = sbrk(heap_size + PTRSIZE); 14 | gc_heaps[0].slot = (Header *)ALIGN((size_t)p, PTRSIZE); 15 | gc_heaps[0].size = heap_size; 16 | gc_heaps[0].slot->size = heap_size; 17 | gc_heaps[0].slot->next_free = NULL; 18 | //将堆初始化到free_list 链表上 19 | gc_free(gc_heaps[0].slot + 1); 20 | } 21 | /** 22 | * 开始进行gc标记 23 | **/ 24 | void* gc_mark(void *ptr){ 25 | GC_Heap *gh; 26 | Header *hdr; 27 | 28 | /* mark check */ 29 | if (!(gh = is_pointer_to_heap(ptr))){ 30 | // printf("not pointer\n"); 31 | return ptr; 32 | } 33 | if (!(hdr = get_header(gh,ptr))) { 34 | // printf("not find header\n"); 35 | return ptr; 36 | } 37 | if (!FL_TEST(hdr, FL_ALLOC)) { 38 | printf("flag not set alloc\n"); 39 | return ptr; 40 | } 41 | if (FL_TEST(hdr, FL_MARK)) { 42 | //printf("flag not set mark\n"); 43 | return ptr; 44 | } 45 | 46 | /* marking */ 47 | FL_SET(hdr, FL_MARK); 48 | //进行子节点递归 标记 49 | for (void* p = ptr; p < (void*)NEXT_HEADER(hdr); p++) { 50 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 51 | //递归进行 引用的拷贝 52 | gc_mark(*(void **)p); 53 | } 54 | return ptr; 55 | } 56 | 57 | /** 58 | * 移动对象 59 | */ 60 | void move_obj() 61 | { 62 | //下面更新引用 63 | size_t i,total; 64 | Header *live; 65 | 66 | //只有一个堆 67 | for (i = 0; i < gc_heaps_used; i++) 68 | { 69 | //右指针,从尾部开始遍历 70 | live = (Header *) ((void * )(gc_heaps[i].slot + 1) + gc_heaps[i].size); 71 | //左指针,从头部开始遍历 72 | free_list = gc_heaps[i].slot; 73 | total = gc_heaps[i].size; 74 | while (true) { 75 | //遍历到第一个非标记的地方,也就是空闲区 76 | while (FL_TEST(free_list, FL_ALLOC) && FL_TEST(free_list, FL_MARK) && free_list < live){ 77 | FL_UNSET(free_list,FL_MARK); 78 | total -= free_list->size; 79 | free_list = NEXT_HEADER(free_list); 80 | } 81 | //遍历到第一个被标记了的地方,这样就会将这个地方拷贝到上面的空闲区 82 | while (!FL_TEST(live, FL_MARK) && live > gc_heaps[i].slot) 83 | //TODO:因为反向遍历的时候 没有域且内存非等分,所以不能通过 -= mem_size 来遍历 84 | live = (Header *) ((void *) live - 1); 85 | //进行拷贝 86 | if (free_list < live) 87 | { 88 | FL_UNSET(live, FL_MARK); 89 | memcpy(free_list, live, live->size); 90 | live->forwarding = free_list; 91 | total -= live->size; 92 | } else { 93 | break; 94 | } 95 | } 96 | } 97 | free_list->size = total; 98 | free_list->next_free = NULL; 99 | //方便测试 把空闲空间都清空 100 | memset(free_list + 1,0,total); 101 | 102 | } 103 | 104 | /** 105 | * 遍历root 106 | * 遍历堆 107 | */ 108 | void adjust_ptr() 109 | { 110 | //遍历所有对象 更新root 111 | for(int i = 0; i < root_used; i ++){ 112 | Header* current = CURRENT_HEADER(roots[i].ptr); 113 | Header* forwarding = current->forwarding; 114 | //注意: 和lisp2不同的是,当前算法不会无差别移动所有对象,那么free_list右边会存在两种指针 115 | // 1. 发生了移动的对象 116 | // 2. 空闲对象 117 | //所以root指向的对象在free_list后面,说明发生了移动,需要更新root 118 | if(current >= free_list){ 119 | roots[i].ptr = forwarding+1; 120 | *(Header**)roots[i].optr = forwarding+1; 121 | } 122 | } 123 | //下面更新引用 124 | size_t i; 125 | Header *p, *pend, *pnext ,*new_obj; 126 | 127 | //当前只有一个堆 128 | for (i = 0; i < gc_heaps_used; i++) 129 | { 130 | //pend 堆内存结束为止 131 | pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size); 132 | 133 | //堆的起始为止 因为堆的内存可能被分成了很多份,所以需要遍历该堆的内存 134 | for (p = gc_heaps[i].slot; p < pend; p = NEXT_HEADER(p)) 135 | { 136 | //可能申请的内存 里面又包含了其他内存 137 | for (void* obj = p+1; obj < (void*)NEXT_HEADER(p); obj++) 138 | { 139 | //正确找到了 child 引用 140 | GC_Heap *gh; 141 | Header *hdr; 142 | if (!(gh = is_pointer_to_heap(*(void**)obj))) continue; 143 | if((hdr = get_header(gh,*(void**)obj))) { 144 | *(Header **) obj = hdr->forwarding + 1; //更新引用 145 | } 146 | } 147 | } 148 | } 149 | 150 | } 151 | void gc(void) 152 | { 153 | printf("start gc()\n"); 154 | //gc 递归标记 155 | for(int i = 0;i < root_used;i++) 156 | gc_mark(roots[i].ptr); 157 | 158 | //移动对象 159 | move_obj(); 160 | //调整指针 161 | adjust_ptr(); 162 | } 163 | 164 | -------------------------------------------------------------------------------- /compact-two-finger/test.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | typedef struct t{ 3 | int value; 4 | struct t* next; 5 | }T; 6 | int clear(){ 7 | 8 | free_list = NULL; 9 | root_used = 0; 10 | } 11 | void test_auto_gc(){ 12 | //初始化4个T空间大小 13 | gc_init(2 * (sizeof(T) + HEADER_SIZE)); 14 | 15 | T* t1 = gc_malloc(sizeof(T)); 16 | assert(t1); 17 | T* t2 = gc_malloc(sizeof(T)); 18 | assert(t2); 19 | 20 | //内存不够了 会自动gc 21 | T* t4 = gc_malloc(sizeof(T)); 22 | assert(t4); 23 | 24 | } 25 | /** 26 | * 测试加入root后的gc情况 27 | */ 28 | void test_add_root_gc(){ 29 | //初始化4个T空间大小 30 | gc_init(2 * (sizeof(T) + HEADER_SIZE)); 31 | 32 | T* t1 = gc_malloc(sizeof(T)); 33 | t1->value = 11; 34 | assert(t1); 35 | add_roots(&t1); 36 | T* t2 = gc_malloc(sizeof(T)); 37 | t2->value = 12; 38 | t1->next = t2; 39 | 40 | //内存不够了 会自动gc 但是gc后依然不够用 41 | T* t4 = gc_malloc(sizeof(T)); 42 | assert(t4 == NULL); 43 | assert(t1->value == 11); 44 | assert(t2->value == 12); 45 | 46 | //将t2去除 47 | t1->next = NULL; 48 | //空间不够 会自动gc 释放 t3 49 | T* t5 = gc_malloc(sizeof(T)); 50 | assert(t1->value == 11); 51 | assert(t2->value == 0); 52 | assert(t5); 53 | } 54 | void main(){ 55 | // 测试不加入 root的时候 会自动进行gc复制 56 | //看看gc压缩的结果是否符合预期 57 | test_auto_gc(); 58 | clear(); 59 | 60 | test_add_root_gc(); 61 | } -------------------------------------------------------------------------------- /conservative.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) 2 | 3 | -------------------------------------------------------------------------------- /copying-or-mark/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) ../gc.c -I ../ 13 | -------------------------------------------------------------------------------- /copying-or-mark/README.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /copying-or-mark/copying.c: -------------------------------------------------------------------------------- 1 | #include "../gc.h" 2 | #include "header.h" 3 | 4 | //每次复制前将 该指针 指向 to的首地址 5 | void* free_p; 6 | int from = 1; 7 | int to = 0; 8 | 9 | /** 10 | * 为了更好的表示算法本身,初始化3个堆 11 | * 1. from 堆 12 | * 2. to 堆 13 | * 3. 专用于标记清除堆 14 | * 这样的空间利用率是2/3,因为to占了1/3,也可以加大标记清除堆的数量更好的利用空间 15 | **/ 16 | void gc_init(size_t heap_size) 17 | { 18 | //关闭扩充堆 19 | auto_grow = 0; 20 | //默认三个堆用于标记清除算法 21 | gc_heaps_used = 3; 22 | 23 | for (size_t i = 0; i < gc_heaps_used; i++){ 24 | //使用sbrk 向操作系统申请大内存块 25 | void* p = sbrk(heap_size + PTRSIZE); 26 | gc_heaps[i].slot = (Header *)ALIGN((size_t)p, PTRSIZE); 27 | gc_heaps[i].size = heap_size; 28 | gc_heaps[i].slot->size = heap_size; 29 | gc_heaps[i].slot->next_free = NULL; 30 | //默认情况下0 是给 to堆使用的 不需要挂载到 free_list 空闲链表上 31 | if(i) gc_free(gc_heaps[i].slot + 1); 32 | } 33 | } 34 | //在gc的时候 from已经全部复制到to堆 35 | //这个时候需要清空from堆,但是再次之前我们需要将free_list空闲指针还保留在from堆上的去除 36 | void remove_from() 37 | { 38 | //遍历所有的空闲链表如果在from堆则去除该引用 39 | Header* prevp = free_list; 40 | 41 | void* from_start = gc_heaps[from].slot; 42 | void* from_end = gc_heaps[from].slot + HEADER_SIZE + gc_heaps[from].size; 43 | for (Header* p = prevp; p ; prevp = p, p = p->next_free) { 44 | //看看当前 p 是否在堆 from上 45 | if((void*)p <= from_end && (void*)p >= from_start) 46 | { 47 | //由可能 free_list 表头就是来自于from堆 48 | if(free_list == prevp) 49 | free_list = p->next_free; 50 | else 51 | prevp->next_free = p->next_free; 52 | } 53 | } 54 | 55 | } 56 | /** 57 | * 进行子对象拷贝 58 | */ 59 | void* gc_copy(void *ptr) 60 | { 61 | Header *hdr; 62 | GC_Heap *gh; 63 | if (!(gh = is_pointer_to_space(ptr,from))) return NULL; 64 | if (!(hdr = get_header(gh,ptr))) return NULL; 65 | 66 | assert(FL_TEST(hdr,FL_ALLOC)); 67 | //没有复制过 0 68 | if(!IS_COPIED(hdr)){ 69 | //计算复制后的指针 70 | Header *forwarding = (Header*)free_p; 71 | //在准备分配前的总空间 72 | size_t total = forwarding->size; 73 | //分配一份内存 将源对象拷贝过来 74 | memcpy(forwarding, hdr, hdr->size); 75 | //拷贝过后 将原对象标记为已拷贝 为后面递归引用复制做准备 76 | FL_SET(hdr,FL_COPIED); 77 | //free 指向下一个 body 78 | free_p += hdr->size; 79 | //free_p 执行的剩余空间需要时刻维护着 80 | ((Header*)free_p)->size = total - hdr->size; 81 | //源对象 保留 新对象的引用 82 | hdr->forwarding = forwarding; 83 | 84 | printf("需要执行拷贝 ptr:%p hdr:%p after:%p\n",ptr,hdr,forwarding); 85 | 86 | //从forwarding 指向的空间开始递归 87 | for (void* p = (void*)(forwarding + 1); p < (void*)NEXT_HEADER(forwarding); p++) { 88 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 89 | //递归进行 引用的拷贝 90 | gc_mark_or_copy(*(void **)p); 91 | } 92 | //返回body 93 | return forwarding + 1; 94 | } 95 | //forwarding 是带有header头部的,返回body即可 96 | return hdr->forwarding+1; 97 | } 98 | /** 99 | * 对该对象进行标记 或拷贝 100 | * 并进行子对象标记 或拷贝 101 | * 返回to空间的 body 102 | * @param ptr 103 | */ 104 | void* gc_mark_or_copy(void* ptr) 105 | { 106 | if(is_pointer_to_space(ptr,from)) 107 | return gc_copy(ptr); 108 | return gc_mark(ptr); 109 | } 110 | /** 111 | * 拷贝引用 112 | * 在将所有的from拷贝到to后 其实 对象的引用还是指向的from,所以需要遍历to对他们进行解引用 113 | */ 114 | void copy_reference() 115 | { 116 | //遍历所有对象 117 | for(int i = 0; i < root_used; i ++) 118 | { 119 | void* start = roots[i].ptr; 120 | void* end = (void*)NEXT_HEADER(CURRENT_HEADER(start)); 121 | 122 | //可能申请的内存 里面又包含了其他内存 123 | for (void *p = start; p < end; p++) { 124 | 125 | Header *hdr; 126 | GC_Heap *gh; 127 | if (!(gh = is_pointer_to_space(*(void**)p,from))) continue; 128 | //解引用 如果该内存依然是指向的from,且有forwarding 则需要改了 129 | if (!(hdr = get_header(gh,*(void**)p))) continue; 130 | if(hdr->forwarding){ 131 | printf("拷贝引用 hdr:%p forwarding:%p\n",hdr,hdr->forwarding); 132 | *(Header**)p = hdr->forwarding + 1; 133 | break; 134 | } 135 | 136 | } 137 | } 138 | } 139 | 140 | /** 141 | * 执行gc 142 | */ 143 | void gc(void) 144 | { 145 | printf("执行gc复制----\n"); 146 | //每次gc前将 free指向 to的开头 147 | gc_heaps[to].slot->size = gc_heaps[to].size; 148 | free_p = gc_heaps[to].slot; 149 | 150 | //递归进行复制 从 from => to 151 | for(int i = 0;i < root_used;i++){ 152 | void* forwarded = gc_mark_or_copy(roots[i].ptr); 153 | *(Header**)roots[i].optr = forwarded; 154 | //将root 所有的执行换到 to上 155 | roots[i].ptr = forwarded; 156 | } 157 | copy_reference(); 158 | 159 | //对标记清除堆执行标记清除算法,from,to堆忽略 160 | gc_sweep(); 161 | //首先将free_p 指向的剩余空间 挂载到空闲链表上 162 | //其实就是将原先to剩余的空间继续利用起来 163 | //如果没有剩余空间了则不进行操作 164 | if(free_p < ((void*)gc_heaps[to].slot + gc_heaps[to].size)) 165 | gc_free((Header*)free_p+1); 166 | //在gc的时候 from已经全部复制到to堆 167 | //这个时候需要清空from堆,但是在此之前我们需要将free_list空闲指针还保留在from堆上的去除 168 | remove_from(); 169 | /** 170 | * 清空 from 空间前: 171 | * 因为空闲链表 还指着from空间的,所以需要更新free_list 指针 172 | * 173 | */ 174 | memset(gc_heaps[from].slot,0,gc_heaps[from].size+HEADER_SIZE); 175 | 176 | //开始交换from 和to 177 | to = from; 178 | from = (from + 1) % 10; 179 | } 180 | 181 | -------------------------------------------------------------------------------- /copying-or-mark/header.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName header 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2020/11/2 0002 下午 4:50 6 | *@Version 1.0 7 | **/ 8 | #ifndef GC_LEARNING_HEADER_H 9 | #define GC_LEARNING_HEADER_H 10 | 11 | extern void* free_p; 12 | extern int from; 13 | extern int to; 14 | 15 | void* gc_mark(void* ptr); 16 | void gc_sweep(void); 17 | 18 | void* gc_mark_or_copy(void* ptr); 19 | void* gc_copy(void *ptr); 20 | int is_pointer_to_from_space(void* ptr); 21 | void remove_from(); 22 | void copy_reference(); 23 | 24 | #endif //GC_LEARNING_HEADER_H 25 | -------------------------------------------------------------------------------- /copying-or-mark/mark-sweep.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | #include "header.h" 3 | 4 | /** 5 | * 开始进行gc标记 6 | **/ 7 | void* gc_mark(void *ptr){ 8 | GC_Heap *gh; 9 | Header *hdr; 10 | //因为外面(mark_or_copy)已经判断过了肯定是来自非 from to 堆 11 | if (!(gh = is_pointer_to_heap(ptr))){ 12 | // printf("not pointer\n"); 13 | return ptr; 14 | } 15 | if (!(hdr = get_header(gh,ptr))) { 16 | // printf("not find header\n"); 17 | return ptr; 18 | } 19 | if (!FL_TEST(hdr, FL_ALLOC)) { 20 | printf("flag not set alloc\n"); 21 | return ptr; 22 | } 23 | if (FL_TEST(hdr, FL_MARK)) { 24 | //printf("flag not set mark\n"); 25 | return ptr; 26 | } 27 | 28 | /* marking */ 29 | FL_SET(hdr, FL_MARK); 30 | // printf("mark ptr : %p, header : %p\n", ptr, hdr); 31 | //进行子节点递归 标记 32 | for (void* p = (void*)(hdr + 1); p < (void*)NEXT_HEADER(hdr); p++) { 33 | //对子类依然需要进行检查是否来自于from,to堆,否则不需要进行标记 34 | gc_mark_or_copy(*(void **)p); 35 | } 36 | return ptr; 37 | } 38 | 39 | /** 40 | * 清除 未标记内存 进行回收利用 41 | */ 42 | void gc_sweep(void) 43 | { 44 | size_t i; 45 | Header *p, *pend, *pnext; 46 | 47 | //遍历所有的堆内存 48 | //因为所有的内存都从堆里申请,所以需要遍历堆找出待回收的内存 49 | for (i = 0; i < gc_heaps_used; i++) { 50 | //to 和 from堆不需要进行清除 51 | if(i == from || i == to) continue; 52 | //pend 堆内存结束为止 53 | pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size); 54 | //堆的起始为止 因为堆的内存可能被分成了很多份,所以需要遍历该堆的内存 55 | for (p = gc_heaps[i].slot; p < pend; p = NEXT_HEADER(p)) { 56 | //查看该堆是否已经被使用 57 | if (FL_TEST(p, FL_ALLOC)) { 58 | //查看该堆是否被标记过 59 | if (FL_TEST(p, FL_MARK)) { 60 | DEBUG(printf("解除标记 : %p\n", p)); 61 | //取消标记,等待下次来回收,如果在下次回收前 62 | //1. 下次回收前发现该内存又被重新访问了,则不需要清除 63 | //2. 下次回收前发现该内存没有被访问过,所以需要清除 64 | FL_UNSET(p, FL_MARK); 65 | }else { 66 | DEBUG(printf("清除回收 :\n")); 67 | gc_free(p+1); 68 | } 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /copying-or-mark/test.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | #include "header.h" 3 | 4 | typedef struct t{ 5 | int value; 6 | struct t* next; 7 | }T; 8 | int clear(){ 9 | free_list = NULL; 10 | to = 0; 11 | from = 1; 12 | root_used = 0; 13 | gc_heaps_used = 0; 14 | free_p = NULL; 15 | } 16 | void test_auto_gc(){ 17 | /** 18 | * 1. 每个堆 一个 T 空间大小 19 | * 2. 总共3个堆 20 | * heaps[0] 作为to 21 | * heaps[1] 作为from 22 | * heaps[2] 作为mark1 23 | */ 24 | gc_init(sizeof(T) + HEADER_SIZE); 25 | assert(to == 0); 26 | assert(from == 1); 27 | 28 | //heaps[1] from分配完了 29 | T* t1 = gc_malloc(sizeof(T)); 30 | t1->value = 1; 31 | //heaps[2] mark分配完了 32 | T* t2 = gc_malloc(sizeof(T)); 33 | t1->next = t2; 34 | t2->value = 2; 35 | assert(t1!=NULL); 36 | assert(t2!=NULL); 37 | assert(t1!=t2); 38 | 39 | //会自动运行gc 40 | //from和 mark1 被回收 41 | //to 变成 heaps[1] 42 | //from 变成 heaps[2] 43 | //mark 变成 heaps[0] 44 | T* t3 = gc_malloc(sizeof(T)); 45 | t3->value = 100; 46 | assert(to == 1); 47 | assert(from == 2); 48 | //t1 t2 都被回收了 49 | //因为gc的时候先将 gc_heaps[3] 进行标记清除,且原本的t2 是从gc_heaps[3]分配的 50 | //所以再次分配的时候 由于空闲链表的设计 t3 依然先从heaps[3]分配,所以 t3==t2 51 | assert(t3 == t2); 52 | 53 | //上面gc的时候 默认情况了两个 T的空间,t3用了一个 那么还剩下一个 54 | //所以正常情况下是还能分配一个 55 | T* t4 = gc_malloc(sizeof(T)); 56 | assert(t3->value==100); 57 | 58 | } 59 | /** 60 | * 测试加入root后的gc情况 61 | */ 62 | void test_add_root_gc(){ 63 | /** 64 | * 1. 每个堆 一个 T 空间大小 65 | * 2. 总共3个堆 66 | * heaps[0] 作为to 67 | * heaps[1] 作为from 68 | * heaps[2] 作为mark1 69 | */ 70 | gc_init(sizeof(T) + HEADER_SIZE); 71 | assert(to == 0); 72 | assert(from == 1); 73 | 74 | //heaps[1] from分配完了 75 | T* t1 = gc_malloc(sizeof(T)); 76 | t1->value = 1; 77 | add_roots(&t1); 78 | //heaps[2] mark分配完了 79 | T* t2 = gc_malloc(sizeof(T)); 80 | t1->next = t2; 81 | t2->value = 2; 82 | assert(t1!=NULL); 83 | assert(t2!=NULL); 84 | assert(t1!=t2); 85 | 86 | //会自动运行gc 87 | //from和 mark1 被回收 88 | //to 变成 heaps[1] 89 | //from 变成 heaps[2] 90 | //mark 变成 heaps[0] 91 | //因为t1 加入root 92 | //且t2 被t1引用 所以不会释放任何内存 93 | //那么t3就会分配失败 94 | T* t3 = gc_malloc(sizeof(T)); 95 | //t3 分配失败 内存不够 96 | assert(t3 == NULL); 97 | assert(to == 1); 98 | assert(from == 2); 99 | //t1 t2 都被回收了 100 | //因为gc的时候先将 gc_heaps[3] 进行标记清除,且原本的t2 是从gc_heaps[3]分配的 101 | //所以再次分配的时候 由于空闲链表的设计 t3 依然先从heaps[3]分配,所以 t3==t2 102 | 103 | 104 | //删除 root 后 可以继续分配 105 | root_used --; 106 | T* t4 = gc_malloc(sizeof(T)); 107 | //t1 t2 都被回收 108 | //t4可以分配 109 | assert(t4 != NULL); 110 | } 111 | void main(){ 112 | // 测试不加入 root的时候 会自动进行gc复制 113 | //看看gc复制的结果是否符合预期 114 | test_auto_gc(); 115 | clear(); 116 | test_add_root_gc(); 117 | 118 | } -------------------------------------------------------------------------------- /copying/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) ../gc.c -I ../ 13 | 14 | -------------------------------------------------------------------------------- /copying/README.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /copying/copying.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | 3 | 4 | //每次复制前将 该指针 指向 to的首地址 5 | void* free_p; 6 | GC_Heap from;//用于分配的区 7 | GC_Heap to; //用于拷贝缓存的区 8 | 9 | /** 10 | * 为了集中于算法的表示,只允许两个堆存在,一个from,一个to 11 | **/ 12 | void gc_init(size_t req_size) 13 | { 14 | auto_gc = 1; 15 | //关闭自动扩充堆 16 | auto_grow = 0; 17 | //使用sbrk 向操作系统申请大内存块 18 | void* from_p = sbrk(req_size + PTRSIZE ); 19 | from.slot = (Header *)ALIGN((size_t)from_p, PTRSIZE); 20 | from.slot->next_free = NULL; 21 | from.slot->size = req_size; 22 | from.size = req_size; 23 | gc_free((void*)(from.slot + 1)); 24 | DEBUG(printf("扩堆内存:%ld ptr:%p\n",req_size,from_p)); 25 | 26 | //使用sbrk 向操作系统申请大内存块 27 | void* to_p = sbrk(req_size + PTRSIZE + HEADER_SIZE); 28 | to.slot = (Header *)ALIGN((size_t)to_p, PTRSIZE); 29 | to.slot->next_free = NULL; 30 | to.slot->size = req_size; 31 | to.size = req_size; 32 | } 33 | //重from堆 检查该指针 34 | Header* get_header_by_from(void *ptr) 35 | { 36 | size_t i; 37 | Header* from_ptr = from.slot; 38 | if ((((void *)from_ptr) > ptr) && ((((void*)from_ptr) + from.size)) <= ptr) { 39 | return NULL; 40 | } 41 | Header *p, *pend, *pnext; 42 | 43 | pend = (Header *)(((void*)(from_ptr)) + from.size); 44 | for (p = from_ptr; p < pend; p = pnext) { 45 | pnext = NEXT_HEADER(p); 46 | if ((void *)(p+1) <= ptr && ptr < (void *)pnext) { 47 | return p; 48 | } 49 | } 50 | return NULL; 51 | } 52 | /** 53 | * 对该对象进行标记 已拷贝 54 | * 并进行子对象标记 已拷贝 55 | * 返回to空间的 body 56 | * @param ptr 57 | */ 58 | void* gc_copy(void * ptr) 59 | { 60 | Header *hdr; 61 | 62 | if (!(hdr = get_header_by_from(ptr))) return NULL; 63 | //没有复制过 0 64 | if(!IS_COPIED(hdr)){ 65 | //计算复制后的指针 66 | Header *forwarding = (Header*)free_p; 67 | //在准备分配前的总空间 68 | size_t total = forwarding->size; 69 | //分配一份内存 将源对象拷贝过来 70 | memcpy(forwarding, hdr, hdr->size); 71 | //标记为已拷贝 72 | FL_SET(hdr,FL_COPIED); 73 | hdr->flags = 1; 74 | //free 指向下一个 body 75 | free_p += hdr->size; 76 | //free_p 执行的剩余空间需要时刻维护着 77 | ((Header*)free_p)->size = total - hdr->size; 78 | //源对象 保留 新对象的引用 79 | hdr->forwarding = forwarding; 80 | 81 | printf("需要执行拷贝 ptr:%p hdr:%p after:%p\n",ptr,hdr,forwarding); 82 | 83 | //从forwarding 指向的空间开始递归 84 | for (void* p = (void*)(forwarding+1); p < (void*)NEXT_HEADER(forwarding); p++) { 85 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 86 | //递归进行 引用的拷贝 87 | gc_copy(*(void **)p); 88 | } 89 | //返回body 90 | return forwarding + 1; 91 | } 92 | //forwarding 是带有header头部的,返回body即可 93 | return hdr->forwarding+1; 94 | } 95 | /** 96 | * 拷贝引用 97 | * 在将所有的from拷贝到to后 其实 对象的引用还是指向的from,所以需要遍历to对他们进行解引用 98 | */ 99 | void copy_reference() 100 | { 101 | //遍历所有对象 102 | for(int i = 0; i < root_used; i ++) 103 | { 104 | void* start = roots[i].ptr; 105 | void* end = (void*)NEXT_HEADER(CURRENT_HEADER(start)); 106 | 107 | //可能申请的内存 里面又包含了其他内存 108 | for (void *p = start; p < end; p++) { 109 | 110 | Header* hdr; 111 | //解引用 如果该内存依然是指向的from,且有forwarding 则需要改了 112 | void *ptr = *(void**)p; 113 | if (!(hdr = get_header_by_from(ptr))) { 114 | continue; 115 | } 116 | if(hdr->forwarding){ 117 | printf("拷贝引用 hdr:%p forwarding:%p\n",hdr,hdr->forwarding); 118 | *(Header**)p = hdr->forwarding + 1; 119 | break; 120 | } 121 | 122 | } 123 | } 124 | } 125 | void gc(void) 126 | { 127 | printf("执行gc复制----\n"); 128 | //每次gc前jiang free指向 to的开头 129 | to.slot->size = to.size; 130 | free_p = to.slot; 131 | 132 | //递归进行复制 从 from => to 133 | for(int i = 0;i < root_used;i++){ 134 | void* forwarded = gc_copy(roots[i].ptr); 135 | *(Header**)roots[i].optr = forwarded; 136 | //将root全部更新到to上 137 | roots[i].ptr = forwarded; 138 | } 139 | //拷贝过后需要更新子类指针引用 140 | copy_reference(); 141 | 142 | //清空 from 143 | memset(from.slot,0,from.size+HEADER_SIZE); 144 | 145 | //开始交换from 和to 146 | Header* tmp = from.slot; 147 | from.slot = to.slot; 148 | to.slot = tmp; 149 | //将空闲链表指向 to的空闲首地址,这时候的to已经被交换为了from 150 | free_list = free_p; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /copying/test.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | typedef struct t{ 3 | int value; 4 | struct t* next; 5 | }T; 6 | int clear(){ 7 | free_list = NULL; 8 | 9 | for (int j = 0; j <= root_used ; ++j){ 10 | roots[j].ptr = NULL; 11 | } 12 | root_used = 0; 13 | } 14 | void test_auto_gc(){ 15 | //分配两个T 空间 16 | gc_init(2* (sizeof(T) + HEADER_SIZE) ); 17 | 18 | T* t1 = gc_malloc(sizeof(T)); 19 | t1->value = 1; 20 | T* t2 = gc_malloc(sizeof(T)); 21 | t2->value = 2; 22 | assert(t1!=NULL); 23 | assert(t2!=NULL); 24 | assert(t1!=t2); 25 | 26 | //因为 t1 t2 没有加入root 27 | //所以在内存不够的时候 会自动gc 进行复制 清除之前的数据 28 | T* t3 = gc_malloc(sizeof(T)); 29 | //t1 t2 都被回收了 30 | assert(t1->value == 0); 31 | assert(t2->value == 0); 32 | //t1 是属于 from堆分配的 33 | //t3 是属于gc后 to堆分配的,如果gc成功后,t3应该不等于t1 34 | assert(t3 != NULL); 35 | assert(t3 != t1); 36 | } 37 | /** 38 | * 加入root后的 gc测试 39 | */ 40 | void test_add_root_gc(){ 41 | //初始化堆 from to 42 | gc_init(TINY_HEAP_SIZE); 43 | 44 | T* p = gc_malloc(sizeof(T)); 45 | T* backup_p = p; 46 | add_roots(&p); 47 | p->next = gc_malloc(sizeof(T)); 48 | p->value = 10; 49 | p->next->value = 20; 50 | T* backup_next = p->next; 51 | //手动触发gc 将 p 和 next 从 from 拷贝到 to 52 | gc(); 53 | 54 | //源地址发生改变 p != p 55 | assert(p != backup_p); 56 | assert(p->next != backup_next); 57 | //原先的值保持不变 58 | assert(p->value == 10); 59 | assert(p->next != NULL); 60 | assert(p->next->value == 20); 61 | 62 | 63 | 64 | } 65 | void main(){ 66 | // 测试不加入 root的时候 会自动进行gc复制 67 | //看看gc复制的结果是否符合预期 68 | test_auto_gc(); 69 | clear(); 70 | 71 | //测试加入root gc复制后 用户态变量地址是否改变 72 | /** 73 | * void *p = gc_malloc() 74 | * gc(); 75 | * p != p 76 | */ 77 | test_add_root_gc(); 78 | } -------------------------------------------------------------------------------- /gc-golang/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(COMMAND cmake_policy) 2 | cmake_policy(SET CMP0003 NEW) 3 | endif(COMMAND cmake_policy) 4 | cmake_minimum_required(VERSION 3.10) 5 | 6 | project(gc) 7 | 8 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -w") 9 | 10 | include_directories(. atomic cache central mgc heap malloc span proc sys) 11 | 12 | add_subdirectory(atomic) 13 | add_subdirectory(cache) 14 | add_subdirectory(central) 15 | add_subdirectory(mgc) 16 | add_subdirectory(heap) 17 | add_subdirectory(malloc) 18 | add_subdirectory(span) 19 | add_subdirectory(proc) 20 | add_subdirectory(sys) 21 | 22 | add_executable(gc test.c) 23 | 24 | target_link_libraries(gc atomic span cache atomic central span mgc heap malloc mgc span proc sys -lpthread) 25 | 26 | -------------------------------------------------------------------------------- /gc-golang/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | mkdir -p bin 3 | cd bin ; cmake .. ; make ; mv gc .. 4 | -------------------------------------------------------------------------------- /gc-golang/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## gc trigger time 3 | 1. [ ] mallocgc() 中本地缓存不够用的时候,会向mcentral中心申请,这种会触发gc 4 | 2. [ ] 定时最多20s 会触发一次gc 5 | 6 | ## sweep trigger time 7 | 8 | 1. [ ] 程序启动时,main阶段会创建一个死循环协程`bgsweep` 一直执行清除、监控任务 9 | 2. [ ] 当用户调用runtime.GC()时,会阻塞gc行为,导致清除阶段也会阻塞进行 10 | 3. [ ] gcStart() 时会去清除上一次gc残留的 span 11 | 4. [ ] 惰性清除。在向中央mcentral申请span时会默认进行 sweep 12 | 5. [ ] 标记结束阶段,会执行抢占所有线程,然后在安全点进行清除每个p的本地mcache 13 | 14 | 15 | ## stw 16 | 1. [ ] 在进入标记阶段 gcoff -> gcmark 时需要stw 17 | 2. [ ] 在标记结束后 gcmark -> gcmarktermination 后需要 stw -------------------------------------------------------------------------------- /gc-golang/atomic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(. srcs) 2 | add_library(atomic ${srcs}) 3 | -------------------------------------------------------------------------------- /gc-golang/atomic/atomic.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName atomic 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/4/2 0002 下午 1:51 6 | *@Version 1.0 7 | **/ 8 | #include "atomic.h" 9 | #include "../sys/gc.h" 10 | #include 11 | #include 12 | 13 | // bool runtime∕internal∕atomic·Cas64(uint64 *val, uint64 old, uint64 new) 14 | // Atomically: 15 | // if(*val == *old){ 16 | // *val = new; 17 | // return 1; 18 | // } else { 19 | // return 0; 20 | // } 21 | //func Casuintptr(ptr *uintptr, old, new uintptr) bool 22 | //bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...) 23 | bool atomic_Cas64(uint64* val,uint64 old, uint64 new) 24 | { 25 | return __sync_bool_compare_and_swap(val,old,new); 26 | } 27 | 28 | bool atomic_Casuintptr(uintptr* ptr,uintptr old,uintptr new) 29 | { 30 | return atomic_Cas64((uint64*)ptr,(uint64)old,(uint64)new); 31 | } 32 | 33 | void atomic_StorepNoWB(void* ptr , void* val) 34 | { 35 | *(void**)ptr = val; 36 | } 37 | void* atomic_Loadp(void* ptr) 38 | { 39 | return *(void**)ptr; 40 | } 41 | uintptr atomic_Xadduintptr(uintptr* ptr, uintptr delta) 42 | { 43 | return atomic_Xadd64(ptr,delta); 44 | } 45 | uintptr atomic_Loaduintptr(uintptr* ptr){ 46 | return *ptr; 47 | } 48 | 49 | /** 50 | * 进行位操作 51 | * @param ptr 52 | * @param val 53 | */ 54 | void atomic_Or8(uint8* ptr, uint8 val) 55 | { 56 | __sync_fetch_and_or(ptr,val); 57 | } 58 | /** 59 | * 60 | * @param val 61 | * @param old 62 | * @return 63 | */ 64 | bool atomic_Cas(int32 *val, int32 old, int32 new) 65 | { 66 | return __sync_bool_compare_and_swap(val,old,new); 67 | } 68 | uint64 atomic_Xadd64(uint64* ptr,int64 delta) 69 | { 70 | return __sync_add_and_fetch(ptr,delta); 71 | } 72 | uint32 atomic_Xadd(uint32* ptr, int32 delta) 73 | { 74 | return __sync_add_and_fetch(ptr,delta); 75 | } 76 | void atomic_Storeuintptr(uintptr* ptr, uintptr new) 77 | { 78 | *ptr = new; 79 | } 80 | void atomic_Store(uint32* ptr,uint32 val) 81 | { 82 | *ptr = val; 83 | } 84 | void atomic_Store64(uint64* ptr,uint64 val) 85 | { 86 | *ptr = val; 87 | } 88 | -------------------------------------------------------------------------------- /gc-golang/atomic/atomic.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName atomic 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/4/2 0002 下午 1:51 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_ATOMIC_H 9 | #define GOC_ATOMIC_H 10 | 11 | #include "../sys/gc.h" 12 | 13 | bool atomic_Cas(int32 *val, int32 old, int32 new); 14 | uint32 atomic_Xadd(uint32* ptr, int32 delta); 15 | uint64 atomic_Xadd64(uint64* ptr,int64 delta); 16 | 17 | void atomic_Store(uint32* ptr,uint32 val); 18 | void atomic_Store64(uint64* ptr,uint64 val); 19 | void atomic_StorepNoWB(void* ptr , void* val); 20 | void atomic_Storeuintptr(uintptr* ptr, uintptr new); 21 | 22 | void atomic_Or8(uint8* ptr, uint8 val); 23 | uintptr atomic_Loaduintptr(uintptr* ptr); 24 | uintptr atomic_Xadduintptr(uintptr* ptr, uintptr delta); 25 | void* atomic_Loadp(void* ptr); 26 | bool atomic_Cas64(uint64* val,uint64 old, uint64 new); 27 | bool atomic_Casuintptr(uintptr* ptr,uintptr old,uintptr new); 28 | #endif //GOC_ATOMIC_H 29 | -------------------------------------------------------------------------------- /gc-golang/cache/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(. srcs) 2 | 3 | add_library(cache ${srcs}) 4 | -------------------------------------------------------------------------------- /gc-golang/cache/README.md: -------------------------------------------------------------------------------- 1 | mcache 2 | -------------------------------------------------------------------------------- /gc-golang/cache/cache.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName cache 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/4/1 0001 下午 5:33 6 | *@Version 1.0 7 | **/ 8 | #include "cache.h" 9 | #include "../span/span.h" 10 | #include "../heap/heap.h" 11 | #include "../atomic/atomic.h" 12 | 13 | span emptyspan; 14 | 15 | /** 16 | * 17 | * @param c 18 | * @param spc 19 | * @param ss 20 | * @param shouldhelpgc 21 | * @return 22 | */ 23 | uintptr cache_nextFree(cache* c,spanclass spc,span** ss,bool* shouldhelpgc) 24 | { 25 | 26 | span* s = c->alloc[spc]; 27 | *shouldhelpgc = false; 28 | uintptr freeIndex = span_nextFreeIndex(s); 29 | 30 | if( freeIndex == s->nelems ){ 31 | // The span is full. 32 | if( (uintptr)s->allocCount != s->nelems ){ 33 | throw("s.allocCount != s.nelems && freeIndex == s.nelems") 34 | } 35 | cache_refill(c,spc); 36 | *shouldhelpgc = true; 37 | s = c->alloc[spc]; 38 | 39 | freeIndex = span_nextFreeIndex(s); 40 | } 41 | 42 | if( freeIndex >= s->nelems ){ 43 | throw("freeIndex is not valid") 44 | } 45 | //找到空闲对象的地址 46 | uintptr v = freeIndex* s->elemsize + s->startaddr; 47 | s->allocCount++; 48 | if( (uintptr)s->allocCount > s->nelems ){ 49 | throw("s.allocCount > s.nelems") 50 | } 51 | return v; 52 | } 53 | /** 54 | * 本地缓存不够用了,需要从中心缓存获取span 55 | * @param c 56 | * @param spc 57 | */ 58 | void cache_refill(cache* c,spanclass spc) 59 | { 60 | // printf("cache not enough!\n"); 61 | heap* h = &heap_; 62 | // Return the current cached span to the central lists. 63 | span* s = c->alloc[spc]; 64 | 65 | if( (uintptr)s->allocCount != s->nelems ){ 66 | throw("refill of span with free space remaining\n" ); 67 | } 68 | if( s != &emptyspan ){ 69 | // Mark this span as no longer cached. 70 | if( s->sweepgen != heap_.sweepgen+3 ){ 71 | throw("bad sweepgen in refill\n") 72 | } 73 | atomic_Store(&s->sweepgen,heap_.sweepgen); 74 | } 75 | 76 | // Get a new cached span from the central lists. 77 | // 本地不够用了,需要从中央链表上进行分配 78 | s = central_cacheSpan(&heap_.centrals[spc]); 79 | if( s == NULL ){ 80 | throw("out of memory") 81 | } 82 | 83 | if( (uintptr)s->allocCount == s->nelems ){ 84 | throw("span has no free space") 85 | } 86 | 87 | // Indicate that this span is cached and prevent asynchronous 88 | // sweeping in the next sweep phase. 表明这个span被缓存,防止在下一个扫描阶段进行异步扫描 89 | s->sweepgen = heap_.sweepgen + 3; 90 | 91 | c->alloc[spc] = s; 92 | } 93 | /** 94 | * 对应: func allocmcache() *mcache 95 | * @return 96 | */ 97 | cache* allocmcache() 98 | { 99 | lock(&heap_.locks); 100 | cache* c = fixalloc_alloc(&heap_.cachealloc); 101 | c->flushGen = heap_.sweepgen; 102 | unlock(&heap_.locks); 103 | //初始化空的span 方便判断 104 | for(int i = 0 ; i < numSpanClasses ; i++){ 105 | c->alloc[i] = &emptyspan; 106 | } 107 | return c; 108 | } 109 | 110 | /** 111 | * 在gc的时候需要刷新所有p本地的m,清空 112 | * @param c 113 | */ 114 | void cache_releaseAll(cache* c) 115 | { 116 | 117 | for(int i = 0 ; i < numSpanClasses ; i++){ 118 | span* s = c->alloc[i]; 119 | if ( s != &emptyspan) { 120 | central_uncacheSpan(&heap_.centrals[i],s); 121 | c->alloc[i] = &emptyspan; 122 | } 123 | } 124 | //这是一个刷新操作,刷新本地缓存后才可以进行清除工作 125 | atomic_Store(&c->flushGen,heap_.sweepgen); 126 | 127 | // Clear tinyalloc pool. 128 | c->tiny = 0; 129 | c->tinyoffset = 0; 130 | } -------------------------------------------------------------------------------- /gc-golang/cache/cache.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName cache 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 下午 3:59 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_CACHE_H 9 | #define GOC_CACHE_H 10 | 11 | #include "../sys/gc.h" 12 | #include "../span/span.h" 13 | 14 | typedef struct mcache cache; 15 | 16 | struct mcache { 17 | //可扫描的堆内字节数 18 | uintptr local_scan; 19 | uintptr tiny; 20 | uintptr tinyoffset; 21 | //记录已经分配的小对象总数 22 | uintptr local_tinyallocs; 23 | 24 | //保存了所有尺寸 指向该尺寸span的链表表头 25 | //当span不够的话 会从mcentral 的noempty中获取,需要加锁了 26 | //二维数组,一位代表各种size尺寸的对象 27 | span* alloc[numSpanClasses]; 28 | // number of frees for small objects (<=maxsmallsize) 29 | uintptr local_nsmallfree[_NumSizeClasses]; 30 | // 表示最后一次刷新本地缓存,如果不等于全局heap_.sweepgen,则说明过旧了,需要刷新后进行清除 31 | uint32 flushGen; 32 | }; 33 | uintptr cache_nextFree(cache* c,spanclass spc,span** ss,bool *shouldhelpgc); 34 | void cache_refill(cache* c,spanclass spc); 35 | cache* allocmcache(); 36 | void cache_releaseAll(cache* c); 37 | #endif //GOC_CACHE_H 38 | -------------------------------------------------------------------------------- /gc-golang/central/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(. srcs) 2 | 3 | add_library(central ${srcs}) 4 | -------------------------------------------------------------------------------- /gc-golang/central/README.md: -------------------------------------------------------------------------------- 1 | mcentral 2 | -------------------------------------------------------------------------------- /gc-golang/central/central.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName central 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 下午 3:36 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_CENTRAL_H 9 | #define GOC_CENTRAL_H 10 | 11 | #include "../sys/gc.h" 12 | #include "../span/span.h" 13 | 14 | typedef struct mcentral central; 15 | 16 | struct mcentral { 17 | mutex locks; 18 | uint8 spanclass; // 规格大小 19 | 20 | spanlist nonempty; // 尚有空闲object的空闲mspan链表 list of spans with a free object, ie a nonempty free list 21 | 22 | spanlist empty; // 没有空闲obect的链表,或者已经被mcache取走了list of spans with no free objects (or cached in an mcache) 23 | // nmalloc is the cumulative count of objects allocated from 统计所有已分配的对象 24 | // this mcentral, assuming all spans in mcaches are 25 | // fully-allocated. Written atomically, read under STW. //操作操作自动更新的,且在stw阶段会读取使用 26 | uint64 nmalloc; 27 | }; 28 | 29 | void central_init(central* c,spanclass i); 30 | span* central_cacheSpan(central* c); 31 | bool central_freeSpan(central* c,span* s,bool preserve,bool wasempty); 32 | span* central_grow(central* c); 33 | void central_uncacheSpan(central* c ,span* s); 34 | #endif //GOC_CENTRAL_H 35 | -------------------------------------------------------------------------------- /gc-golang/heap/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(. srcs) 2 | 3 | add_library(heap ${srcs}) 4 | -------------------------------------------------------------------------------- /gc-golang/heap/README.md: -------------------------------------------------------------------------------- 1 | heap 2 | -------------------------------------------------------------------------------- /gc-golang/heap/arena.c: -------------------------------------------------------------------------------- 1 | #include "arena.h" 2 | #include "heap.h" 3 | #include "../sys/gc.h" 4 | #include "../malloc/lineralloc.h" 5 | #include "../sys/sysapi.h" 6 | #include "../atomic/atomic.h" 7 | 8 | 9 | uint arenaIndex(uintptr p) 10 | { 11 | return (uint)((p + arenaBaseOffset) / heapArenaBytes); 12 | } 13 | uint arena_l1(uint i){ 14 | if(arenaL1Bits == 0) return 0; 15 | return i >> arenaL1Shift; 16 | } 17 | uint arena_l2(uint i){ 18 | if(arenaL1Bits == 0) return i; 19 | return i & (1 << arenaL2Bits - 1); 20 | } 21 | 22 | // pageIndexOf returns the arena, page index, and page mask for pointer p. 23 | // The caller must ensure p is in the heap. 24 | heapArena* pageIndexOf(uintptr p,uintptr * pageIdx,uint8* pageMask) 25 | { 26 | uint ai = arenaIndex(p); 27 | //获取对应的 heap堆 28 | array* arr = &heap_.arenas[arena_l1(ai)]; 29 | heapArena** arena = ARRAY_GET(arr,arena_l2(ai)); 30 | *pageIdx = ((p / pageSize) / 8) % (uintptr)(pagesPerArena/8); 31 | *pageMask = (byte)(1 << ((p / pageSize) % 8)); 32 | return *arena; 33 | } 34 | /** 35 | * 申请一份 heap area 大内存块 36 | * 也可以叫做 arean alloc 37 | * @param n 38 | * @param ssize 返回申请了多少字节 39 | * @return 40 | */ 41 | void* heap_sysAlloc(uintptr n,uintptr* ssize) 42 | { 43 | uintptr size; 44 | heap* h = &heap_; 45 | n = round(n, heapArenaBytes);//进行字节对齐操作 46 | 47 | // First, try the arena pre-reservation. 48 | void* v = linearalloc_alloc(&h->arena,n,heapArenaBytes); 49 | if( v != NULL ){ 50 | size = n; 51 | goto mapped; 52 | } 53 | 54 | // Try to grow the heap at a hint address. 55 | while( h->arenaHints != NULL ){ 56 | arenaHint* hint = h->arenaHints; 57 | uintptr p = hint->addr; 58 | if( hint->down ){ 59 | p -= n; 60 | } 61 | if( p+n < p ){ 62 | // We can't use this, so don't ask. 63 | v = NULL; 64 | } else if( arenaIndex(p+n-1) >= 1< 72 | if( !hint->down ){ 73 | p += n; 74 | } 75 | hint->addr = p; 76 | size = n; 77 | break; 78 | } 79 | if( v != NULL ){ 80 | sys_free(v, n); 81 | } 82 | h->arenaHints = hint->next; 83 | fixalloc_free(&h->arenaHintAlloc,hint); 84 | } 85 | 86 | if( size == 0 ){ 87 | // All of the hints failed, so we'll take any 88 | // (sufficiently aligned) address the kernel will give 89 | // us. 90 | void* v; 91 | uintptr size = n; 92 | 93 | v = sys_reserveAligned(NULL,&size,heapArenaBytes); 94 | if( v == NULL ){ 95 | *ssize = 0; 96 | return NULL; 97 | } 98 | 99 | // Create new hints for extending this region. 100 | arenaHint* hint = fixalloc_alloc(&h->arenaHintAlloc); 101 | hint->addr = v; 102 | hint->down = true; 103 | //将当前的arena插入链表头 104 | hint->next = h->arenaHints; 105 | h->arenaHints = hint; 106 | 107 | hint = fixalloc_alloc(&h->arenaHintAlloc); 108 | hint->addr = v + size; 109 | hint->down = true; 110 | //将当前的arena插入链表头 111 | hint->next = h->arenaHints; 112 | h->arenaHints = hint; 113 | } 114 | 115 | // Check for bad pointers or pointers we can't use. 116 | { 117 | char* bad = ""; 118 | uintptr p = v; 119 | if( p+size < p ){ 120 | bad = "region exceeds uintptr range"; 121 | } else if( arenaIndex(p) >= 1<= 1<arenas[arena_l1(ri)]; 146 | if( l2->addr == NULL ){ 147 | // Allocate an L2 arena map. 148 | //直接向操作系统分配一块大 arena内存块 149 | //分配一个 heapArea** 数组 150 | array_init(l2,1 << arenaL2Bits,ptrSize); 151 | if( l2->addr == NULL ){ 152 | throw("out of memory allocating heap arena map") 153 | } 154 | } 155 | heapArena** initHa = ARRAY_GET(l2,arena_l2(ri)); 156 | if( *initHa != NULL ){ 157 | throw("arena already initialized") 158 | } 159 | heapArena* r; 160 | //申请一个 heapaRrena 结构体用于管理上面申请的 64M area 大内存 161 | r = (heapArena*)(sys_fixalloc(sizeof(heapArena), ptrSize)); 162 | if( r == NULL ){ 163 | throw("out of memory allocating heap arena metadata") 164 | } 165 | 166 | 167 | // Add the arena to the arenas list. 168 | uint8* insert = array_push(&h->allarenas); 169 | *insert = ri; 170 | 171 | //add new area to areas 172 | *initHa = r; 173 | } 174 | 175 | *ssize = size; 176 | return v; 177 | } 178 | 179 | -------------------------------------------------------------------------------- /gc-golang/heap/arena.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName arena 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 下午 2:27 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_ARENA_H 9 | #define GOC_ARENA_H 10 | 11 | #include "../sys/gc.h" 12 | #include "../span/span.h" 13 | typedef struct marenaHint arenaHint; 14 | typedef struct mheapArena heapArena; 15 | 16 | 17 | //每个arena 64Mb 连续空间 18 | struct marenaHint{ 19 | //addr 起始地址 20 | uintptr addr; 21 | uint8 down; 22 | arenaHint* next; 23 | }; 24 | 25 | //TODO: 数组相关的需要提前初始化 26 | struct mheapArena { 27 | //位图, 用来优化gc标记的一块内存 28 | //用2mb位图 来标记 arena 64m的使用情况 29 | byte bitmap[heapArenaBitmapBytes]; 30 | //8192 个span,每个页是8kb 但是一个span可能包含多个页 span.nPages 表示总共多少个页 31 | span* spans[pagesPerArena]; 32 | 33 | //是一个位图,使用1024 * 8 bit来标记 8192个页(8192*8KB = 64MB)中哪些页正在使用中; 34 | //uint8 35 | uint8 pageInuse[pagesPerArena/8]; 36 | //标记页 与gc相关 37 | uint8 pageMarks[pagesPerArena/8]; 38 | }; 39 | 40 | 41 | heapArena* pageIndexOf(uintptr p,uintptr * pageIdx,uint8* pageMask); 42 | uint arenaIndex(uintptr p); 43 | //linux平台的heap.arenas 一位数组容量为1 ,windows下为64 44 | uint arena_l1(uint i); 45 | uint arena_l2(uint i); 46 | void* heap_sysAlloc(uintptr n,uintptr* ssize); 47 | 48 | #endif //GOC_ARENA_H 49 | -------------------------------------------------------------------------------- /gc-golang/heap/heap.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName heap 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 上午 11:11 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_HEAP_H 9 | #define GOC_HEAP_H 10 | 11 | #include "../sys/gc.h" 12 | #include "../sys/array.h" 13 | #include "treap.h" 14 | #include "arena.h" 15 | #include "../malloc/malloc.h" 16 | #include "../malloc/fixalloc.h" 17 | #include "../malloc/lineralloc.h" 18 | #include "../central/central.h" 19 | #include "../mgc/gc_sweepbuf.h" 20 | typedef struct mheap heap; 21 | 22 | 23 | 24 | // Main malloc heap 主堆 type mheap struct 25 | struct mheap { 26 | //线程锁 27 | mutex locks; 28 | 29 | //下面是两个二叉树 存储的span 30 | treap free; // free and non-scavenged spans 空闲和未回收的spans 31 | treap scav; 32 | 33 | //包含了所有的span* 34 | array allspans; 35 | 36 | //包含两个span栈 37 | //0 包含了已经被清除过了,但任然在使用中的span 38 | //1 包含了没有被清除过,但任然在使用中的span 39 | //每次heap_.sweepgen周期+2 后会自动更换位置 40 | gcSweepBuf sweepSpans[2]; 41 | //各种分配器 42 | //span* 分配器 43 | fixalloc spanalloc; 44 | //cache* 分配器 45 | fixalloc cachealloc; 46 | //大对象 分配器 47 | fixalloc treapalloc; 48 | fixalloc specialfinalizeralloc; 49 | fixalloc specialprofilealloc; 50 | // allocator for arenaHints 51 | fixalloc arenaHintAlloc; 52 | //仅在32位系统上才会用到? arena 是预留的空间,用于分配堆 arena(实际的 arena)。 这仅用于 32 位。 53 | lineralloc arena; 54 | 55 | //保存了所有的arena的索引 uint8 56 | array allarenas; 57 | //全局堆保存了所有的 arena ,并且用链表串起来 58 | arenaHint* arenaHints; 59 | 60 | //arenas [6] *[20]*heapArena 61 | //存储二维heapAreana 62 | // heapArena** arenas; 63 | array arenas[1 << arenaL1Bits]; 64 | 65 | //并发内存分配与并发gc之间的 负载均衡 66 | uintptr scavengeCredit; 67 | // 如果sweepgen == mheap.sweepgen - 2, span需要扫描 68 | // 如果sweepgen == mheap.sweepgen - 1, span正在被扫描 69 | // 如果sweepgen == mheap.sweepgen, span已经扫描完成,等待被使用 70 | // 如果sweepgen == mheap.sweepgen + 1, span已经被缓存,现在仍然缓存中,则需要扫描 71 | // 如果sweepgen == mheap.sweepgen + 3, span已经扫描过了,还处在缓存中 72 | uint32 sweepgen; // sweep generation, see comment in span.Mspan 73 | uint32 sweepdone;// 判断清除是否完成 74 | uint32 sweepers; //目前有多少span正在执行清理工作 75 | void** dweepSpans; 76 | 77 | uint64 pagesInuse; 78 | //统计大对象已经申请的字节数 79 | uint64 largealloc; // bytes allocated for large objects 80 | //统计已经申请的大对象个数 81 | uint64 nlargealloc; // number of large object allocations 82 | 83 | //中央空闲链表数组 采用 2*67个空间来存储多尺寸对象,一半存储指针对象 另一半存储非指针对象, heap里其实是维护了134个central,这134个central对应了 mcache 中的 alloc 数组,也就是每一个spanClass就有一个central。 所以,在mcache中申请内存时,如果在某个 spanClass 的内存链表上找不到空闲内存,那么 mcache 就会向对应的 spanClass 的central获取一批内存块。 注意,这里central数组的定义里面使用填充字节,这是因为多线程会并发访问不同central避免false sharing。 84 | central centrals[numSpanClasses]; 85 | 86 | }; 87 | 88 | 89 | //全局堆管理器 90 | extern heap heap_; 91 | 92 | void heap_init(); 93 | span* heap_alloc(uintptr npage, uint8 spanclass, bool large , bool needzero); 94 | span* heap_alloc_m(uintptr npage,spanclass sc,bool large); 95 | span* heap_pickFreeSpan(uintptr npage); 96 | span* heap_allocSpanLocked(uintptr npage); 97 | void heap_freeSpanLocked(span* s,bool acctinuse,bool acctidle,int64 unusedsince); 98 | void heap_freeSpan(span* s , bool large); 99 | void heap_coalesce(span* s); 100 | bool heap_grow(uintptr npage); 101 | 102 | void heap_setSpan(uintptr base,span* s); 103 | void heap_setSpans(uintptr base,uintptr npage,span* s ); 104 | span* heap_spanOf(uintptr p); 105 | void heap_scavengeLargest(uintptr nbytes); 106 | span* heap_allocManual(uintptr npage); 107 | #endif //GOC_HEAP_H 108 | -------------------------------------------------------------------------------- /gc-golang/heap/treap.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName treap 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 上午 11:12 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_TREAP_H 9 | #define GOC_TREAP_H 10 | 11 | #include "../sys/gc.h" 12 | #include "../span/span.h" 13 | 14 | typedef struct mtreap treap; 15 | typedef struct mtreapNode treapNode; 16 | 17 | /** 18 | * 红黑树,用于存储活跃的span页 19 | * 在本地缓存不够用的时候会向中心缓存获取 20 | */ 21 | struct mtreap{ 22 | //执向头节点 23 | treapNode* root; 24 | }; 25 | 26 | //heap堆结构 27 | struct mtreapNode{ 28 | 29 | treapNode* right; 30 | treapNode* left; 31 | treapNode* parent; 32 | 33 | size_t npagesKey; // number of pages in spanKey, used as primary sort key 34 | span* spankey; 35 | // 优先级 random number used by treap algorithm to keep tree probabilistically balanced treap 算法使用随机数来保持树的概率平衡 36 | uint32 priority; 37 | }; 38 | 39 | treapNode* treap_find(treap* root,uintptr npages); 40 | void treap_removeNode(treap* root,treapNode* t); 41 | void treap_removeSpan(treap* root , span* s); 42 | void treap_insert(treap* root,span* s); 43 | treapNode* treap_end(treap* root); 44 | 45 | treapNode* treap_pred(treapNode* t); 46 | #endif //GOC_TREAP_H 47 | -------------------------------------------------------------------------------- /gc-golang/malloc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(. srcs) 2 | 3 | add_library(malloc ${srcs}) 4 | -------------------------------------------------------------------------------- /gc-golang/malloc/fixalloc.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName fixalloc 3 | *@Author brewlin 4 | *@Date 2021/3/29 0029 下午 3:48 5 | *@Version 1.0 6 | **/ 7 | 8 | #include "fixalloc.h" 9 | #include "../sys/sysapi.h" 10 | 11 | palloc globalAlloc; 12 | mutex ga_lock; 13 | void* persistentChunks; 14 | /** 15 | * 16 | * @param size 17 | * @param func 18 | */ 19 | void fixalloc_init(fixalloc* f,uintptr size,firstp first,void* arg){ 20 | f->size = size; 21 | f->first = first; 22 | 23 | f->arg = arg; 24 | f->list = NULL; 25 | f->chunk = 0; 26 | f->nchunk = 0; 27 | f->inuse = 0; 28 | f->zero = true; 29 | 30 | } 31 | /** 32 | * 33 | * @param f 34 | * @param p 35 | */ 36 | void fixalloc_free(fixalloc* f,uintptr* p) 37 | { 38 | f->inuse -= f->size; 39 | mlink* v = (mlink*)p; 40 | //插入链表头 O(1); 41 | v->next = f->list; 42 | f->list = v; 43 | } 44 | 45 | /** 46 | * 固定分配f->size, 对应: func (f *fixalloc) alloc() 47 | * @param f 48 | * @return 49 | */ 50 | uintptr* fixalloc_alloc(fixalloc* f) 51 | { 52 | void *v; 53 | if (f->size == 0 ){ 54 | printf("runtime: use of FixAlloc_Alloc before FixAlloc_Init\n"); 55 | printf("runtime: internal error\n"); 56 | exit(-1); 57 | } 58 | 59 | if (f->list != NULL ) { 60 | v = (void*)f->list; 61 | f->list = f->list->next; 62 | f->inuse += f->size; 63 | if (f->zero ) { 64 | memset(v,0,f->size); 65 | } 66 | return v; 67 | } 68 | if ((uintptr)f->nchunk < f->size ) { 69 | //内存不够了 70 | f->chunk = (uintptr)sys_alloc(fixAllocChunk); 71 | f->nchunk = fixAllocChunk; 72 | } 73 | 74 | v = (void*)f->chunk; 75 | //只有span 分配的时候才会有这个钩子 76 | if (f->first != NULL ) { 77 | f->first(f->arg, v); // -> recordspan 78 | } 79 | f->chunk = f->chunk + f->size; 80 | f->nchunk -= (uint32)(f->size); 81 | f->inuse += f->size; 82 | return v; 83 | } -------------------------------------------------------------------------------- /gc-golang/malloc/fixalloc.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName fixalloc 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 下午 2:05 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_FIXALLOC_H 9 | #define GOC_FIXALLOC_H 10 | 11 | #include "../sys/gc.h" 12 | 13 | typedef struct _mlink mlink; 14 | typedef void (*firstp)(void* arg,void* p); // 函数指针 15 | typedef struct mpalloc palloc; 16 | typedef struct mfixalloc fixalloc; 17 | 18 | struct _mlink { 19 | mlink* next; 20 | }; 21 | 22 | 23 | 24 | //固定分配, fixalloc 是slab风格的分配器,用于分配固定大小的对象。 fixalloc分配的对象可以被释放,但是这个内存可能会被fixalloc pool复用, 因此它只能给相同类型的对象复用 25 | struct mfixalloc { 26 | uintptr size; 27 | 28 | firstp first; // called first time p is returned 29 | void* arg; 30 | 31 | mlink* list; //一个通用链表 指向下一个 空闲空余内存 32 | 33 | uintptr chunk; // use uintptr instead of unsafe.Pointer to avoid write barriers 34 | uint32 nchunk; 35 | 36 | //记录在使用中的bytes 37 | uintptr inuse; 38 | uint8 zero; // zero allocations 39 | }; 40 | 41 | struct mpalloc { 42 | void* base; 43 | uintptr off; 44 | }; 45 | 46 | //TODO: init 全局分配器 47 | extern palloc globalAlloc; 48 | extern mutex ga_lock; 49 | extern void* persistentChunks; 50 | 51 | //初始化 52 | void fixalloc_init(fixalloc* f,uintptr size,firstp first,void* arg); 53 | void fixalloc_free(fixalloc* f,uintptr* p); 54 | uintptr* fixalloc_alloc(fixalloc* f); 55 | 56 | #endif //GOC_FIXALLOC_H 57 | -------------------------------------------------------------------------------- /gc-golang/malloc/lineralloc.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName lineralloc 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/31 0031 下午 4:18 6 | *@Version 1.0 7 | **/ 8 | #include "lineralloc.h" 9 | #include "../sys/gc.h" 10 | #include "../sys/sysapi.h" 11 | #include "malloc.h" 12 | 13 | /** 14 | * 15 | * @param l 16 | * @param size 17 | * @param align 18 | * @return 19 | */ 20 | void* linearalloc_alloc(lineralloc* l,uintptr size,uintptr align) 21 | { 22 | 23 | uintptr p = round(l->next, align); 24 | if ( p+size > l->end ){ 25 | return NULL; 26 | } 27 | l->next = p + size; 28 | uintptr pEnd = round(l->next-1, physPageSize); 29 | if ( pEnd > l->mapped ){ 30 | // We need to map more of the reserved space. 31 | sys_map(l->mapped,pEnd-l->mapped); 32 | l->mapped = pEnd; 33 | } 34 | return p; 35 | } -------------------------------------------------------------------------------- /gc-golang/malloc/lineralloc.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName lineralloc 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 下午 2:21 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_LINERALLOC_H 9 | #define GOC_LINERALLOC_H 10 | 11 | #include "../sys/gc.h" 12 | 13 | typedef struct{ 14 | uintptr next;// next free byte 15 | uintptr mapped;// one byte past end of mapped space 16 | uintptr end;// end of reserved space 17 | }lineralloc; 18 | void* linearalloc_alloc(lineralloc* l,uintptr size,uintptr align); 19 | 20 | #endif //GOC_LINERALLOC_H 21 | -------------------------------------------------------------------------------- /gc-golang/malloc/malloc.c: -------------------------------------------------------------------------------- 1 | #include "../heap/heap.h" 2 | #include "../mgc/bitmap.h" 3 | #include "malloc.h" 4 | #include "../sys/gpm.h" 5 | // 物理页大小 6 | uintptr physPageSize; 7 | 8 | //初始化 arean区域 heap span 等 9 | // 由schedinit调用,进程启动后会进入这里 10 | // mallocinit 实现内存分配的一些初始化,检查对象规格大小对照表, 11 | // 还将连续的虚拟地址,划分成三大块: 12 | /* 13 | 512MB 16GB 512GB 14 | +-------+-------------+-------------------+ 15 | | spans | bitmap | arena | 16 | +-------+-------------+-------------------+ 17 | */ 18 | // 这三个区域可以按需同步线性扩张,无须预分配内存 19 | void mallocinit() 20 | { 21 | //初始化堆 Initialize the heap -> mheap_.init() 22 | heap_init(); 23 | 24 | //给当前mcache 设置缓存 25 | _g_ = malloc(sizeof(g)); 26 | _g_->m = malloc(sizeof(m)); 27 | _g_->m->mallocing = 0; 28 | _g_->m->mcache = allocmcache(); // 对应: func allocmcache() *mcache 29 | 30 | //初始成员变量 31 | array_init(&heap_.allspans,ARRAY_SIZE, sizeof(span*)); 32 | array_init(&heap_.allarenas,ARRAY_SIZE, sizeof(uint8)); 33 | mutex_init(&heap_.locks); 34 | 35 | //初始化预留的内存 36 | for ( int i = 0x7f; i >= 0; i-- ){ 37 | uintptr p ; 38 | //只支持linux 39 | p = (uintptr )i<<40 | uintptrMask&(0x00c0<<32); 40 | arenaHint* hint = fixalloc_alloc(&heap_.arenaHintAlloc); 41 | hint->addr = p; 42 | //串联成链表 43 | hint->next = heap_.arenaHints; 44 | heap_.arenaHints = hint; 45 | } 46 | 47 | } 48 | 49 | /** 50 | * 51 | * @param size 52 | * @param needzero 53 | * @param noscan 54 | * @return 55 | */ 56 | span* largeAlloc(uintptr size, uint8 needzero, uint8 noscan) 57 | { 58 | 59 | //检测是否内存溢出 60 | if( size + pageSize < size ){ 61 | throw("out of memory\n"); 62 | } 63 | uintptr npages; 64 | span* s; 65 | 66 | npages = size >> pageShift; //计算页索引 67 | if( size & PageMask != 0 ) { 68 | npages ++; 69 | } 70 | //开始分配大内存了 71 | s = heap_alloc(npages, makeSpanClass(0,noscan), true, needzero); 72 | if( s == NULL ){ 73 | //如果直接从全局堆上无法获取到内存块的话 就溢出了 74 | throw("out of memory\n"); 75 | } 76 | s->limit = s->startaddr + size; 77 | heapBits h = hb_heapBitsForAddr(s->startaddr); 78 | hb_initSpan(h,s); 79 | return s; 80 | } 81 | -------------------------------------------------------------------------------- /gc-golang/malloc/malloc.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName malloc 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 上午 11:00 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_MALLOC_H 9 | #define GOC_MALLOC_H 10 | 11 | #include "../sys/gc.h" 12 | #include "../span/span.h" 13 | 14 | extern uintptr physPageSize; 15 | span* largeAlloc(uintptr size, uint8 needzero, uint8 noscan); 16 | void* mallocgc(uintptr size,type* typ,bool needzero); 17 | void mallocinit(); 18 | 19 | #endif //GOC_MALLOC_H 20 | -------------------------------------------------------------------------------- /gc-golang/mgc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_language(ASM) 2 | 3 | aux_source_directory(. srcs) 4 | 5 | add_library(mgc ${srcs} root.s) 6 | -------------------------------------------------------------------------------- /gc-golang/mgc/README.md: -------------------------------------------------------------------------------- 1 | gc -------------------------------------------------------------------------------- /gc-golang/mgc/bitarena.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 2021/4/3. 3 | // 4 | 5 | #include "bitarena.h" 6 | #include "../atomic/atomic.h" 7 | #include "../sys/sysapi.h" 8 | #include "../sys/gc.h" 9 | 10 | //全局 位图 分配器 11 | _gcBitsArenas gcBitsArenas; 12 | 13 | uint8* bitarena_tryAlloc(gcBitsArena* b ,uintptr bytes) 14 | { 15 | //len(b->bits) 16 | uintptr bitslen = gcBitsChunkBytes - gcBitsHeaderBytes; 17 | if ( b == NULL || atomic_Loaduintptr(&b->free)+bytes > bitslen) 18 | { 19 | return NULL; 20 | } 21 | // Try to allocate from this block. 22 | uintptr end = atomic_Xadduintptr(&b->free, bytes); 23 | if ( end > bitslen) { 24 | return NULL; 25 | } 26 | // There was enough room. 27 | uintptr start = end - bytes; 28 | return &b->bits[start]; 29 | } 30 | 31 | /** 32 | * 33 | * 位图arena不够分配位图了,需要扩充arena堆 34 | * 这里会短暂的释放锁,所以必须调用方先持有锁 35 | * @return 36 | */ 37 | gcBitsArena* bitarena_newArenaMayUnlock() 38 | { 39 | gcBitsArena* result; 40 | if ( gcBitsArenas.free == NULL) { 41 | unlock(&gcBitsArenas.locks); 42 | //向操作系统申请一份 43 | result = sys_alloc(gcBitsChunkBytes); 44 | if (result == NULL ) { 45 | throw("runtime: cannot allocate memory") 46 | } 47 | lock(&gcBitsArenas.locks); 48 | } else { 49 | result = gcBitsArenas.free; 50 | gcBitsArenas.free = gcBitsArenas.free->next; 51 | //清零 52 | memset(result,0,gcBitsChunkBytes); 53 | } 54 | result->next = NULL; 55 | result->free = 0; 56 | return result; 57 | } 58 | -------------------------------------------------------------------------------- /gc-golang/mgc/bitarena.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 2021/4/3. 3 | // 4 | 5 | #ifndef GOC_BITARENA_H 6 | #define GOC_BITARENA_H 7 | 8 | #include "../sys/gc.h" 9 | 10 | /** 11 | * type gcBitsHeader struct { 12 | free uintptr // free is the index into bits of the next free byte. 13 | next uintptr // *gcBits triggers recursive type bug. (issue 14620) 14 | } 15 | */ 16 | #define gcBitsChunkBytes ((uintptr)(64 << 10)) 17 | //sizeof(gcBitsHeader{}) 18 | #define gcBitsHeaderBytes 16 19 | 20 | typedef struct _gcBitsArena gcBitsArena; 21 | /** 22 | * 专用于位图的分配器 23 | */ 24 | struct _gcBitsArena 25 | { 26 | // gcBitsHeader // side step recursive type bug (issue 14620) by including fields by hand. 27 | uintptr free;// free is the index into bits of the next free byte; read/write atomically 28 | 29 | gcBitsArena* next; 30 | uint8 bits[gcBitsChunkBytes - gcBitsHeaderBytes]; 31 | }; 32 | 33 | typedef struct { 34 | mutex locks; 35 | gcBitsArena* free; 36 | gcBitsArena* next; 37 | gcBitsArena* current; 38 | gcBitsArena* previous; 39 | }_gcBitsArenas; 40 | 41 | extern _gcBitsArenas gcBitsArenas; 42 | 43 | 44 | uint8* bitarena_tryAlloc(gcBitsArena* b ,uintptr bytes); 45 | gcBitsArena* bitarena_newArenaMayUnlock(); 46 | 47 | 48 | 49 | #endif //GOC_BITARENA_H 50 | -------------------------------------------------------------------------------- /gc-golang/mgc/bitmap.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 2021/4/3. 3 | // 4 | 5 | #ifndef GOC_BITMAP_H 6 | #define GOC_BITMAP_H 7 | 8 | #include "../sys/gc.h" 9 | #include "../span/span.h" 10 | 11 | typedef struct { 12 | uint8* bitp; 13 | uint32 shift; 14 | uint32 arena; 15 | uint8* last; 16 | }heapBits; 17 | 18 | heapBits hb_heapBitsForAddr(uintptr addr); 19 | heapBits hb_forward(heapBits h,uintptr n); 20 | heapBits hb_forwardOrBoundary(heapBits h,uintptr n,uintptr* nw); 21 | void hb_initSpan(heapBits h,span* s); 22 | 23 | uint8* newMarkBits(uintptr nelems); 24 | void nextMarkBitArenaEpoch(); 25 | #endif //GOC_BITMAP_H 26 | -------------------------------------------------------------------------------- /gc-golang/mgc/drain.c: -------------------------------------------------------------------------------- 1 | #include "../sys/gc.h" 2 | #include "gc_work.h" 3 | #include "../sys/gpm.h" 4 | #include "root.h" 5 | 6 | //线程tls 栈范围 7 | __thread uintptr stk_start; 8 | __thread uintptr stk_end; 9 | //扫描根 10 | //扫描栈 11 | 12 | /** 13 | * 传入一个队列,里面所有对象都是灰色对象 14 | * @param gcw 15 | */ 16 | void gcDrain(gcWork* gcw) 17 | { 18 | //初始化栈范围 19 | stk_end = get_sp(); 20 | 21 | //高低往低地址增长 22 | uintptr cur_sp = stk_end; 23 | if(stk_start <= stk_end) throw("stack error!") 24 | for (; cur_sp < stk_start ; cur_sp += 1){ 25 | span* s; 26 | uintptr objIndex; 27 | uintptr base = findObject(*(void**)cur_sp,&s,&objIndex); 28 | //找到灰色对象 29 | if(base != 0){ 30 | greyobject(base, s,gcw,objIndex); 31 | } 32 | } 33 | 34 | 35 | //开始标记剩余需要扫描的灰色对象 36 | while(true){ 37 | uintptr obj = gcwork_tryGetFast(gcw); 38 | if(obj == NULL){ 39 | obj = gcwork_tryGet(gcw); 40 | if(obj == NULL) 41 | break; 42 | } 43 | span* s = NULL; 44 | uintptr objIndex; 45 | uintptr base = findObject(obj,&s,&objIndex); 46 | if(base == 0) continue; 47 | //开始扫描 48 | uintptr size = s->elemsize; 49 | for(uintptr i = base ; i < obj + size; i++){ 50 | if(findObject(*(void**)i,&s,&objIndex) != 0){ 51 | printf("markone:%p\t",*(void**)i); 52 | greyobject(*(void**)i,s,gcw,objIndex); 53 | } 54 | } 55 | printf("\n\n"); 56 | 57 | } 58 | 59 | //标记结束 60 | 61 | } -------------------------------------------------------------------------------- /gc-golang/mgc/gc.c: -------------------------------------------------------------------------------- 1 | #include "../sys/gc.h" 2 | #include "../sys/gpm.h" 3 | #include "gc.h" 4 | #include "../atomic/atomic.h" 5 | #include "gc_work.h" 6 | #include "../heap/heap.h" 7 | #include "bitmap.h" 8 | #include "../proc/proc.h" 9 | 10 | mutex worldsema; 11 | /** 12 | * 开始gc,将状态gcoff 转换为 gcmark阶段 13 | * @return 14 | */ 15 | void gcStart() 16 | { 17 | // 多个线程并发的获取 剩余未清除的span,进行垃圾清除 18 | // 防止gc跨代了 19 | while(gcphase == _GCoff && sweepone() != ~(uintptr)0){} 20 | if(work.gcing > 0) { 21 | sighandler(0); 22 | return; 23 | } 24 | 25 | lock(&worldsema); 26 | atomic_Xadd(&work.gcing,1); 27 | 28 | if(gcphase != _GCoff){ 29 | unlock(&worldsema); 30 | atomic_Xadd(&work.gcing,-1); 31 | return; 32 | } 33 | // 进入并发标记阶段,开启写屏障 34 | gcphase = _GCmark; 35 | work.userForced = true; 36 | stopTheWorld(&allm,ncpu); 37 | //开始标记 38 | gcWork gcw; 39 | gcwork_init(&gcw); 40 | gcDrain(&gcw); 41 | //标记结束 42 | gcMarkDone(); 43 | startTheWorld(); 44 | // atomic_Xadd(&work.gcing,-1); 45 | work.gcing = 0; 46 | unlock(&worldsema); 47 | 48 | } 49 | /** 50 | * 标记阶段结束 ——> 标记终止状态 51 | * 所有可达对象都标记为黑色对象,无灰色对象存在 52 | * 当前函数在多线程下只有最后一个标记线程会触发调用(所有线程都陷入等待,那么表明所有标记任务完成) work->nwait == work->nproc && !gcMarkWorkAvailable(p) 53 | * @return 54 | */ 55 | void gcMarkDone() 56 | { 57 | //FIXME: 确保只有一个线程能进入这里 semacquire(&work->markDoneSema); 58 | if( gcphase != _GCmark ){ 59 | // semrelease(&work->markDoneSema); 60 | return; 61 | } 62 | // Perform mark termination. This will restart the world. 63 | gcMarkTermination(); 64 | } 65 | 66 | /** 67 | * 同样只有一个线程会触发当前方法 68 | * 标记阶段转移为标记结束阶段 69 | * 标记终止 -> gc终止状态 70 | * @param float64 71 | * @return 72 | */ 73 | void gcMarkTermination() 74 | { 75 | // World is stopped. 76 | // Start marktermination which includes enabling the write barrier. 77 | atomic_Store(&gcBlackenEnabled, false); 78 | atomic_Store(&gcphase,_GCmarktermination); 79 | //转换为_GCoff阶段 80 | atomic_Store(&gcphase,_GCoff); 81 | gcSweep(); 82 | 83 | for(int i = 0; i < ncpu ; i ++){ 84 | if(allm[i] == NULL)continue; 85 | cache_releaseAll(allm[i]->mcache); 86 | } 87 | } 88 | 89 | /** 90 | * 当前gc阶段完全结束,开始关闭写屏障 91 | * 将当前全局 sweepgen += 2 结束当前轮 92 | * 注意: 这里的结束并非是清除结束,清除阶段是有一些惰性的操作存在 93 | * 如果是用户层面调用的gc,则需要立即清除全部 94 | * @param gcMode 95 | * @return 96 | */ 97 | void gcSweep() 98 | { 99 | if( gcphase != _GCoff ){ 100 | throw("gcSweep being done but phase is not GCoff") 101 | } 102 | 103 | lock(&heap_.locks); 104 | //gc标志+2,和 span的sweepgen 相互影响 105 | heap_.sweepgen += 2; 106 | heap_.sweepdone = 0; 107 | if( heap_.sweepSpans[heap_.sweepgen/2%2].index != 0 ){ 108 | throw("non-empty swept list") 109 | } 110 | unlock(&heap_.locks); 111 | 112 | //如果是阻塞 gc模式 需要立即清扫 113 | if(work.userForced){ 114 | // Sweep all spans eagerly. 115 | while( sweepone() != ~(uintptr)0 ){} 116 | //标记结束,可以关闭写屏障了 117 | } 118 | return; 119 | 120 | } 121 | 122 | /** 123 | * 从队列里选取一个进行清理,在多线程下会被多个线程竞争进行并发清理 124 | * 采用原子操作,优化锁竞争 125 | * @return 126 | */ 127 | uintptr sweepone() 128 | { 129 | g* _g_ = getg(); 130 | if( heap_.sweepdone != 0 ){ 131 | return ~(uintptr)0; 132 | } 133 | atomic_Xadd(&heap_.sweepers, 1); 134 | 135 | span* s ; 136 | uint32 sg = heap_.sweepgen; 137 | while(true){ 138 | s = gcsweepbuf_pop(&heap_.sweepSpans[1-sg/2%2]); 139 | //所有的span都清理完毕 140 | if( s == NULL ){ 141 | atomic_Store(&heap_.sweepdone, 1); 142 | break; 143 | } 144 | if( s->state != mSpanInUse ){ 145 | //如果清扫已经清扫过的对象 就会发送一次 146 | if( !(s->sweepgen == sg || s->sweepgen == sg+3) ){ 147 | throw("non in-use span in unswept list"); 148 | } 149 | continue; 150 | } 151 | //找到了一个需要清理的span,手动修改标志位为正在清理中 152 | if( s->sweepgen == sg-2 && atomic_Cas(&s->sweepgen, sg-2, sg-1) ){ 153 | break; 154 | } 155 | } 156 | 157 | //清理我们找到的span 158 | uintptr npages = ~(uintptr)0; 159 | if( s != NULL ){ 160 | npages = s->npages; 161 | if( span_sweep(s,false)){ 162 | //被释放了 163 | } else { 164 | //span任然在使用 165 | npages = 0; 166 | } 167 | } 168 | atomic_Xadd(&heap_.sweepers, -1); 169 | return npages; 170 | } 171 | 172 | /** 173 | * 需要运行在独立的协程或者线程中,不停的唤醒-睡眠 174 | */ 175 | void bgsweep() 176 | { 177 | // sweep.g = getg(); 178 | 179 | // lock(&sweep.lock) 180 | // sweep.parked = true 181 | // goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1) 182 | 183 | while(true){ 184 | //每清除一个span,就主动让出一下线程 185 | while( sweepone() != ~(uintptr)0 ){} 186 | } 187 | } -------------------------------------------------------------------------------- /gc-golang/mgc/gc.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName gc 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/4/12 0012 下午 1:46 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_GC_GC_H 9 | #define GOC_GC_GC_H 10 | 11 | #include "../sys/gc.h" 12 | #include "../span/span.h" 13 | 14 | //gcstart 15 | void gcStart(); 16 | //多个线程只有最后一个标记线程会触发标记结束 17 | void gcMarkDone(); 18 | void gcMarkTermination(); 19 | void gcSweep(); 20 | uintptr sweepone(); 21 | void bgsweep(); 22 | bool span_sweep(span* s,bool preserve); 23 | 24 | extern __thread uintptr stk_start; 25 | extern __thread uintptr stk_end; 26 | 27 | #endif //GOC_GC_H 28 | -------------------------------------------------------------------------------- /gc-golang/mgc/gc_sweepbuf.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName gc_sweepbuf 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/4/12 0012 下午 4:36 6 | *@Version 1.0 7 | **/ 8 | #include "gc_sweepbuf.h" 9 | #include "../span/span.h" 10 | #include "../atomic/atomic.h" 11 | #include "../sys/sysapi.h" 12 | 13 | #define gcSweepBlockEntries 512 // 4KB on 64-bit 14 | #define gcSweepBufInitSpineCap 256 // Enough for 1GB heap on 64-bit 15 | #define CacheLinePadSize 64 16 | 17 | typedef struct { 18 | span* spans[gcSweepBlockEntries]; 19 | }gcSweepBlock; 20 | 21 | /** 22 | * 23 | * @param b 24 | * @param s 25 | */ 26 | void gcsweepbuf_push(gcSweepBuf* b,span* s) 27 | { 28 | 29 | // Obtain our slot. 30 | uint32 cursor = (uintptr)(atomic_Xadd(&b->index, +1) - 1); 31 | uint32 top = cursor / gcSweepBlockEntries; 32 | uint32 bottom = cursor%gcSweepBlockEntries; 33 | 34 | // Do we need to add a block? 35 | uintptr spineLen = atomic_Loaduintptr(&b->spineLen); 36 | gcSweepBlock* block; 37 | 38 | retry: 39 | if ( top < spineLen ) 40 | { 41 | void* spine = atomic_Loadp(&b->spine); 42 | void* blockp = spine + ptrSize*top; 43 | block = (gcSweepBlock*)(atomic_Loadp(blockp)); 44 | } else { 45 | lock(&b->spineLock); 46 | spineLen = atomic_Loaduintptr(&b->spineLen); 47 | if (top < spineLen ){ 48 | unlock(&b->spineLock); 49 | goto retry; 50 | } 51 | //满了 52 | if (spineLen == b->spineCap ){ 53 | // Grow the spine. 54 | uintptr newCap = b->spineCap * 2; 55 | if (newCap == 0 ){ 56 | newCap = gcSweepBufInitSpineCap; 57 | } 58 | //申请一块堆外的持久化内存 59 | void* newSpine = sys_fixalloc(newCap*ptrSize,64); 60 | if (b->spineCap != 0 ){ 61 | // Blocks are allocated off-heap, so 62 | // no write barriers. 63 | memmove(newSpine, b->spine, b->spineCap*ptrSize); 64 | } 65 | // Spine is allocated off-heap, so no write barrier. 66 | atomic_StorepNoWB(&b->spine, newSpine); 67 | b->spineCap = newCap; 68 | // FIXME: 这里说的是old内存没有多大,即使泄漏了也没有影响 69 | } 70 | 71 | block = sys_fixalloc(sizeof(gcSweepBlock),CacheLinePadSize); 72 | void* blockp = b->spine + ptrSize*top; 73 | // Blocks are allocated off-heap, so no write barrier. 74 | atomic_StorepNoWB(blockp, block); 75 | atomic_Storeuintptr(&b->spineLen, spineLen+1); 76 | unlock(&b->spineLock); 77 | } 78 | // We have a block. Insert the span. 79 | block->spans[bottom] = s; 80 | } 81 | 82 | // pop removes and returns a span from buffer b, or NULL if b is empty. 83 | // pop is safe to call concurrently with other pop operations, but NOT 84 | // to call concurrently with push. 85 | span* gcsweepbuf_pop(gcSweepBuf* b) 86 | { 87 | uint32 cursor = atomic_Xadd(&b->index, -1); 88 | if ((int32)cursor < 0 ){ 89 | atomic_Xadd(&b->index, +1); 90 | return NULL; 91 | } 92 | // There are no concurrent spine or block modifications during 93 | // pop, so we can omit the atomics. 94 | uint32 top = cursor / gcSweepBlockEntries; 95 | uint32 bottom = cursor%gcSweepBlockEntries; 96 | gcSweepBlock** blockp = b->spine + ptrSize*top; 97 | gcSweepBlock* block = *blockp; 98 | span* s = block->spans[bottom]; 99 | // Clear the pointer for block(i). 100 | block->spans[bottom] = NULL; 101 | return s; 102 | } 103 | -------------------------------------------------------------------------------- /gc-golang/mgc/gc_sweepbuf.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName gc_sweepbuf 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/4/12 0012 下午 4:36 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_GC_SWEEPBUF_H 9 | #define GOC_GC_SWEEPBUF_H 10 | 11 | #include "../sys/gc.h" 12 | #include "../span/span.h" 13 | 14 | typedef struct 15 | { 16 | mutex spineLock; 17 | void* spine; // *[N]*gcSweepBlock, accessed atomically 18 | uintptr spineLen; // Spine array length, accessed atomically 19 | uintptr spineCap; // Spine array cap, accessed under lock 20 | 21 | uint32 index; 22 | }gcSweepBuf; 23 | 24 | void gcsweepbuf_push(gcSweepBuf* b,span* s); 25 | span* gcsweepbuf_pop(gcSweepBuf* b); 26 | 27 | #endif //GOC_GC_SWEEPBUF_H 28 | -------------------------------------------------------------------------------- /gc-golang/mgc/gc_work.h: -------------------------------------------------------------------------------- 1 | #ifndef GOC_GC_WORK_H 2 | #define GOC_GC_WORK_H 3 | 4 | #include "../sys/gc.h" 5 | #include "../span/span.h" 6 | 7 | typedef struct _gcWork gcWork; 8 | 9 | typedef struct { 10 | uint64 next; 11 | uintptr pushcnt; 12 | }lfnode; 13 | 14 | #define _WorkbufSize 2048 15 | #define _workbufObjsize ((_WorkbufSize-24) / ptrSize) 16 | typedef struct { 17 | lfnode node; 18 | int nobj; 19 | // account for the above fields 20 | uintptr obj[_workbufObjsize]; 21 | }workbuf; 22 | 23 | struct _gcWork { 24 | workbuf *wbuf1,*wbuf2; 25 | 26 | //统计当前工作缓冲区已经标记为黑色的字节数量 27 | uint64 bytesMarked; 28 | int64 scanWork; 29 | bool flushedWork; 30 | uint32 pauseGen; 31 | uint32 putGen; 32 | uintptr pauseStack[16]; 33 | }; 34 | 35 | typedef struct { 36 | mutex locks;//TODO: init 37 | spanlist free; 38 | spanlist busy; 39 | }workerSpans; 40 | 41 | typedef struct { 42 | uint64 full; 43 | uint64 empty; 44 | 45 | //是否是强制型GC 46 | bool userForced; 47 | //gc执行了几轮 48 | uint32 cycles; 49 | workerSpans wbufSpans; 50 | uint32 gcing; 51 | }_work; 52 | 53 | extern _work work; 54 | 55 | bool gcwork_putFast(gcWork* w,uintptr obj); 56 | void gcwork_put(gcWork* w, uintptr obj); 57 | void greyobject(uintptr obj, span *s, gcWork* gcw, uintptr objIndex); 58 | uintptr findObject(uintptr p, span** ss,uintptr* objIndex); 59 | uintptr gcwork_tryGet(gcWork* w); 60 | uintptr gcwork_tryGetFast(gcWork* w); 61 | void gcDrain(gcWork* gcw); 62 | void gcwork_init(gcWork* w); 63 | #endif //GOC_GC_WORK_H 64 | -------------------------------------------------------------------------------- /gc-golang/mgc/mark.c: -------------------------------------------------------------------------------- 1 | #include "../sys/gc.h" 2 | #include "mark.h" 3 | #include "../span/span.h" 4 | #include "../heap/heap.h" 5 | #include "../atomic/atomic.h" 6 | 7 | bool useCheckmark = false; 8 | 9 | // setMarked sets the marked bit in the markbits, atomically. 10 | void mb_setMarked(markBits* m){ 11 | atomic_Or8(m->bytep,m->mask); 12 | } 13 | /** 14 | * 标记新分配的内存为黑色(针对在gc阶段中产生的新内存) 15 | * @param obj 16 | * @param size 17 | * @param scanSize 18 | */ 19 | void gc_marknewobject(uintptr obj,uintptr size,uintptr scanSize) 20 | { 21 | 22 | if ( useCheckmark ) { // The world should be stopped so this should not happen. 23 | throw("gcmarknewobject called while doing checkmark") 24 | } 25 | markBits tmb = gc_markBitsForAddr(obj); 26 | mb_setMarked(&tmb); 27 | // gcw := &getg().m.p.ptr().gcw 28 | // gcw.bytesMarked += (uint64)(size) 29 | // gcw.scanWork += (int64)scanSize) 30 | } 31 | /** 32 | * @param s 33 | * @param p 34 | * @return 35 | */ 36 | uintptr span_objIndex(span* s ,uintptr p) 37 | { 38 | uintptr byteOffset = p - s->startaddr; 39 | if (byteOffset == 0 ){ 40 | return 0; 41 | } 42 | if (s->basemask != 0 ){ 43 | // s.baseMask is non-0, elemsize is a power of two, so shift by s.divShift 44 | return byteOffset >> s->divshift; 45 | } 46 | return (uintptr)(((uint64)(byteOffset) >> s->divshift) * (uint64)(s->divmul) >> s->divshift2); 47 | } 48 | /** 49 | * 50 | * @param p 51 | * @return 52 | */ 53 | markBits gc_markBitsForAddr(uintptr p) 54 | { 55 | //找出p对应的span 56 | span* s = heap_spanOf(p); 57 | uintptr objIndex = span_objIndex(s,p); 58 | return span_markBitsForIndex(s,objIndex); 59 | } 60 | 61 | /** 62 | * 63 | * @param s 64 | * @param objIndex 65 | * @return 66 | */ 67 | markBits span_markBitsForIndex(span* s,uintptr objIndex) 68 | { 69 | //每个位对应一个对象,所以找到该对象所属的 字节 70 | uint8* bytep = s->gcmarkBits + (objIndex/8); 71 | //找到上面字节中对应于哪个位 72 | uint8 mask = 1 << (objIndex%8); 73 | 74 | markBits mb = {bytep,mask,objIndex}; 75 | return mb; 76 | } -------------------------------------------------------------------------------- /gc-golang/mgc/mark.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 2021/4/3. 3 | // 4 | 5 | #ifndef GOC_MARK_H 6 | #define GOC_MARK_H 7 | 8 | #include "../sys/gc.h" 9 | #include "../span/span.h" 10 | 11 | typedef struct { 12 | uint8* bytep; 13 | uint8 mask; 14 | uintptr index; 15 | }markBits; 16 | 17 | //gc 18 | markBits gc_markBitsForAddr(uintptr p); 19 | void gc_marknewobject(uintptr obj,uintptr size,uintptr scanSize); 20 | 21 | //span 22 | uintptr span_objIndex(span* s ,uintptr p); 23 | markBits span_markBitsForIndex(span* s,uintptr objIndex); 24 | markBits span_markBitsForBase(span* s); 25 | markBits span_allocBitsForIndex(span* s ,uintptr allocBitIndex); 26 | 27 | 28 | uint8* newMarkBits(uintptr nelems); 29 | void bit_heapBitsSetType(uintptr x, uintptr size, uintptr dataSize , type* typ); 30 | 31 | //markBits 32 | void mb_setMarkedNonAtomic(markBits* m); 33 | void mb_advance(markBits* m); 34 | #endif //GOC_MARK_H 35 | -------------------------------------------------------------------------------- /gc-golang/mgc/root.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName root 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2020/11/6 0006 下午 3:18 6 | *@Version 1.0 7 | **/ 8 | #ifndef GC_LEARNING_ROOT_H 9 | #define GC_LEARNING_ROOT_H 10 | 11 | void* get_sp()asm("get_sp"); 12 | void* get_bp()asm("get_bp"); 13 | void* get_di()asm("get_di"); 14 | void* get_si()asm("get_si"); 15 | void* get_dx()asm("get_dx"); 16 | void* get_cx()asm("get_cx"); 17 | void* get_r8()asm("get_r8"); 18 | void* get_r9()asm("get_r9"); 19 | void* get_bp()asm("get_bp"); 20 | void* get_ax()asm("get_ax"); 21 | void* get_bx()asm("get_bx"); 22 | 23 | #endif //GC_LEARNING_ROOT_H 24 | -------------------------------------------------------------------------------- /gc-golang/mgc/root.s: -------------------------------------------------------------------------------- 1 | .text 2 | 3 | .globl get_sp 4 | get_sp: 5 | movq %rsp,%rax 6 | ret 7 | 8 | .globl get_bp 9 | get_bp: 10 | movq %rbp,%rax 11 | ret 12 | 13 | .globl get_di 14 | get_di: 15 | movq %rdi,%rax 16 | ret 17 | 18 | .globl get_si 19 | get_si: 20 | movq %rsi,%rax 21 | ret 22 | 23 | .globl get_dx 24 | get_dx: 25 | movq %rdx,%rax 26 | ret 27 | 28 | .globl get_cx 29 | get_cx: 30 | movq %rcx,%rax 31 | ret 32 | 33 | .globl get_r8 34 | get_r8: 35 | movq %r8,%rax 36 | ret 37 | 38 | .globl get_r9 39 | get_r9: 40 | movq %r9,%rax 41 | ret 42 | 43 | .globl get_ax 44 | get_ax: 45 | movq %rax,%rax 46 | ret 47 | 48 | .globl get_bx 49 | get_bx: 50 | movq %rbx,%rax 51 | ret 52 | -------------------------------------------------------------------------------- /gc-golang/mgc/scanstack.c: -------------------------------------------------------------------------------- 1 | #include "bitmap.h" 2 | #include "../sys/gc.h" 3 | #include "bitarena.h" 4 | #include "../sys/gpm.h" 5 | #include "../heap/heap.h" 6 | #include "mark.h" 7 | #include "../atomic/atomic.h" 8 | #include "../heap/arena.h" 9 | #include "gc_work.h" 10 | 11 | /** 12 | * 如果obj还没有被标记过,则直接进行标记为灰色 丢入队列里 等待转为黑色 13 | * @param obj 14 | * @param s 15 | * @param gcw 16 | * @param objIndex 17 | */ 18 | void greyobject(uintptr obj, span *s, gcWork* gcw, uintptr objIndex) 19 | { 20 | //obj 必须是对象的首地址,所以这里要检查下是否对齐 21 | if ( obj & (ptrSize-1) != 0) { 22 | throw("greyobject: obj not pointer-aligned") 23 | } 24 | //找到对象对应的标记位图 25 | markBits mbits = span_markBitsForIndex(s,objIndex); 26 | //再次检查一下该对象状态是否正常 27 | if(span_isFree(s,objIndex)){ 28 | throw("marking free object") 29 | } 30 | //看看是否已经标记过了 31 | if(*mbits.bytep & mbits.mask != 0){ 32 | return; 33 | } 34 | //开始标记 35 | atomic_Or8(mbits.bytep, mbits.mask); 36 | 37 | // Mark span. 38 | uintptr pageIdx; 39 | uint8 pageMask; 40 | heapArena* arena = pageIndexOf(s->startaddr,&pageIdx,&pageMask); 41 | // FIXME: 标记span ? 没搞懂意思 42 | if (arena->pageMarks[pageIdx] & pageMask == 0 ){ 43 | atomic_Or8(&arena->pageMarks[pageIdx], pageMask); 44 | } 45 | //如果当前对象 为不需要扫描的类型,则直接转换为黑色,完成标记过程 46 | if( (s->spanclass&1) != 0){ 47 | gcw->bytesMarked += (uint64)s->elemsize; 48 | return; 49 | } 50 | //将当前需要扫描的对象加入队列,等待跟进一步的扫描 51 | if(!gcwork_putFast(gcw,obj)){ 52 | gcwork_put(gcw,obj); 53 | } 54 | } 55 | /** 56 | * 找到地址p对于与堆里对象的起始地址,然后再是其对应的span,span中的索引 57 | * @param p 58 | * @param ss 59 | * @param objIndex 60 | * @return uintptr 61 | */ 62 | uintptr findObject(uintptr p, span** ss,uintptr* objIndex) 63 | { 64 | *objIndex = 0; 65 | span* s = heap_spanOf(p); 66 | *ss = s; 67 | uintptr base = 0; 68 | // If p is a bad pointer, it may not be in s's bounds. 69 | if (s == NULL || p < s->startaddr || p >= s->limit || s->state != mSpanInUse ){ 70 | return NULL; 71 | } 72 | // If this span holds object of a power of 2 size, just mask off the bits to 73 | // the interior of the object. Otherwise use the size to get the base. 74 | if ( s->basemask != 0 ){ 75 | // optimize for power of 2 sized objects. 76 | base = s->startaddr; 77 | base = base + ((p-base)&((uintptr)s->basemask)); 78 | *objIndex = (base - s->startaddr) >> s->divshift; 79 | // base = p & s.baseMask is faster for small spans, 80 | // but doesn't work for large spans. 81 | // Overall, it's faster to use the more general computation above. 82 | } else { 83 | base = s->startaddr; 84 | //如果计算出来的p所属的对象首地址异常,则根据objid 从新计算首地址(难道不是应该丢弃么?) 85 | if ( p-base >= s->elemsize ){ 86 | // n := (p - base) / s.elemsize, using division by multiplication 87 | *objIndex = (uintptr)(p-base) >> s->divshift * (uintptr)s->divmul >> s->divshift2; 88 | base += *objIndex * s->elemsize; 89 | } 90 | } 91 | return base; 92 | } 93 | /** 94 | * 通常用于扫描 非 heap根 95 | * @param uintptr 96 | * @param uint8 97 | * @param gcWork 98 | * @param stackScanState 99 | * @return 100 | */ 101 | //void scanblock(uintptr b0,uintptr n0,uint8* ptrmask, gcWork* gcw,stackScanState* stk) 102 | //{ 103 | // 104 | // // Use local copies of original parameters, so that a stack trace 105 | // // due to one of the throws below shows the original block 106 | // // base and extent. 107 | // uintptr b = b0; 108 | // uintptr n = n0; 109 | // 110 | // for ( uintptr i = 0; i < n; ){ 111 | // // Find bits for the next word. 112 | // uint32 bits = *(ptrmask+(i/(ptrSize*8))); 113 | // if ( bits == 0 ){ 114 | // i += ptrSize * 8; 115 | // continue 116 | // } 117 | // for ( int j = 0; j < 8 && i < n; j++ ){ 118 | // if ( bits&1 != 0 ){ 119 | // // Same work as in scanobject; see comments there. 120 | // uintptr p = *(uintptr*)(b + i); 121 | // if ( p != 0 ) { 122 | // span* s; 123 | // uintptr objIndex; 124 | // uintptr obj = findObject(p,&s,&objIndex); 125 | // 126 | // //如果不属于heap对象,则可能是栈对象 127 | //// if(obj != 0){ 128 | // greyobject(obj,span, gcw, objIndex); 129 | //// } else if stk != NULL && p >= stk.stack.lo && p < stk.stack.hi { 130 | //// stk.putPtr(p) 131 | //// } 132 | // } 133 | // } 134 | // bits >>= 1; 135 | // i += ptrSize; 136 | // } 137 | // } 138 | //} 139 | -------------------------------------------------------------------------------- /gc-golang/mgc/sweep.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName sweep 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/4/2 0002 下午 2:07 6 | *@Version 1.0 7 | **/ 8 | #include "../sys/gc.h" 9 | #include "../heap/heap.h" 10 | #include "../atomic/atomic.h" 11 | #include "../span/span.h" 12 | #include "../sys/gpm.h" 13 | #include "gc_sweepbuf.h" 14 | #include "bitmap.h" 15 | 16 | 17 | /** 18 | * 19 | * 1. 对未被标记的白色span进行回收处理,并放回heap且连接到mcentral链表上 20 | * 2. 对span只进行初始化工作,初始化对应位图为下次gc做准备 21 | * @param s 22 | * @param preserve 23 | * @return 24 | */ 25 | bool span_sweep(span* s,bool preserve) 26 | { 27 | heap* h = &heap_; 28 | //当前清理工作是防止抢占的,在没有清理完之前防止进入下一轮gc 29 | g* _g_ = getg(); 30 | // if( _g_->m->mallocing == 0 && _g_ != _g_->m->g0 ){ 31 | // throw("mspan.sweep: m is not locked") 32 | // } 33 | uint32 sweepgen = h->sweepgen; 34 | if( s->state != mSpanInUse || s->sweepgen != sweepgen-1 ){ 35 | throw("mspan.sweep: bad span state") 36 | } 37 | // FIXME: gc count 38 | // atomic_Xadd64(&h->pagesSwept, (int64)s->npages)); 39 | 40 | spanclass spc = s->spanclass; 41 | uintptr size = s->elemsize; 42 | bool res = false; 43 | 44 | cache* c = _g_->m->mcache; 45 | bool freeToHeap = false; 46 | 47 | 48 | // Count the number of free objects in this span. 49 | uint16 nalloc = (uint16)(span_countAlloc(s)); 50 | if( sizeclass(spc) == 0 && nalloc == 0 ){ 51 | s->needzero = 1; 52 | freeToHeap = true; 53 | } 54 | uintptr nfreed = s->allocCount - nalloc; 55 | if( nalloc > s->allocCount ){ 56 | throw("sweep increased allocation count") 57 | } 58 | 59 | s->allocCount = nalloc; 60 | bool wasempty = span_nextFreeIndex(s) == s->nelems; 61 | //重置分配索引 0 62 | s->freeindex = 0; 63 | 64 | // gcmarkBits becomes the allocBits. 65 | // get a fresh cleared gcmarkBits in preparation for next GC 66 | s->allocBits = s->gcmarkBits; 67 | s->gcmarkBits = newMarkBits(s->nelems); 68 | 69 | // 初始化位图缓存 从0 开始 70 | span_refillAllocCache(s,0); 71 | 72 | // We need to set s.sweepgen = h.sweepgen only when all blocks are swept, 73 | // because of the potential for a concurrent free/SetFinalizer. 74 | // But we need to set it before we make the span available for allocation 75 | // (return it to heap or mcentral), because allocation code assumes that a 76 | // span is already swept if available for allocation. 77 | if( freeToHeap || nfreed == 0 ){ 78 | // The span must be in our exclusive ownership until we update sweepgen, 79 | // check for potential races. 80 | if( s->state != mSpanInUse || s->sweepgen != sweepgen-1 ){ 81 | throw("mspan.sweep: bad span state after sweep") 82 | } 83 | // Serialization point. 84 | // At this point the mark bits are cleared and allocation ready 85 | // to go so release the span. 86 | atomic_Store(&s->sweepgen,sweepgen); 87 | } 88 | 89 | if( nfreed > 0 && sizeclass(spc) != 0 ){ 90 | c->local_nsmallfree[sizeclass(spc)] += (uintptr)(nfreed); 91 | 92 | res = central_freeSpan(&h->centrals[spc],s,preserve, wasempty); 93 | // mcentral->freeSpan updates sweepgen 94 | } else if( freeToHeap ){ 95 | heap_freeSpan(s,true); 96 | // c->local_nlargefree++; 97 | // c->local_largefree += size; 98 | res = true; 99 | } 100 | if( !res ){ 101 | //当前span已经被清除过了,但span还可以继续使用,入栈继续监控 102 | gcsweepbuf_push(&h->sweepSpans[sweepgen/2%2],s); 103 | } 104 | return res; 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /gc-golang/proc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(. srcs) 2 | 3 | add_library(proc ${srcs}) 4 | -------------------------------------------------------------------------------- /gc-golang/proc/README.md: -------------------------------------------------------------------------------- 1 | proc -------------------------------------------------------------------------------- /gc-golang/proc/proc.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@Deacription go 3 | *@Author brewlin 4 | *@Date 2021/4/1 0001 下午 3:39 5 | *@Version 1.0 6 | **/ 7 | #include "../sys/gc.h" 8 | #include "../sys/gpm.h" 9 | #include 10 | #include 11 | #include 12 | #include "../atomic/atomic.h" 13 | __thread int mid; 14 | 15 | //全局条件变量 16 | pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; 17 | void sighandler(int signo); 18 | /** 19 | * 20 | */ 21 | void regsig() 22 | { 23 | struct sigaction actions; 24 | sigemptyset(&actions.sa_mask); 25 | /* 将参数set信号集初始化并清空 */ 26 | actions.sa_handler = sighandler; 27 | actions.sa_flags = -1; 28 | sigaction(SIGURG,&actions,NULL); 29 | } 30 | /** 31 | * 32 | * @param signo 33 | */ 34 | void sighandler(int signo) 35 | { 36 | //不可靠信号需要重新安装 37 | regsig(); 38 | //等待主线程释放锁 39 | allm[mid]->mallocing ++; 40 | pthread_mutex_lock(&mtx); 41 | pthread_mutex_unlock(&mtx); 42 | allm[mid]->mallocing --; 43 | return; 44 | } 45 | /** 46 | * 47 | * @param allm 48 | * @param ms 49 | */ 50 | void stopTheWorld(m** allm , int ms) 51 | { 52 | pthread_mutex_lock(&mtx); 53 | //这里发送信号其他所有线程 54 | for(int i = 0 ; i < ms ; i ++){ 55 | if(allm[i] != NULL && allm[i]->mid != mid && allm[i]->mallocing == 0){ 56 | pthread_kill(allm[i]->pid,SIGURG); 57 | } 58 | } 59 | } 60 | /** 61 | * 62 | */ 63 | void startTheWorld() 64 | { 65 | pthread_mutex_unlock(&mtx); 66 | } 67 | -------------------------------------------------------------------------------- /gc-golang/proc/proc.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 2021/4/17. 3 | // 4 | 5 | #ifndef GOC_PROC_H 6 | #define GOC_PROC_H 7 | 8 | #include 9 | #include "../sys/gpm.h" 10 | 11 | void stopTheWorld(m** allm , int ms); 12 | void startTheWorld(); 13 | void regsig(); 14 | void sighandler(int signo); 15 | 16 | 17 | extern __thread int mid; 18 | 19 | #endif //GOC_PROC_H 20 | -------------------------------------------------------------------------------- /gc-golang/readme: -------------------------------------------------------------------------------- 1 | git clean -dxf -------------------------------------------------------------------------------- /gc-golang/span/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(. srcs) 2 | 3 | add_library(span ${srcs}) 4 | -------------------------------------------------------------------------------- /gc-golang/span/README.md: -------------------------------------------------------------------------------- 1 | mspan 2 | -------------------------------------------------------------------------------- /gc-golang/span/span.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName span 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 上午 11:27 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_SPAN_H 9 | #define GOC_SPAN_H 10 | #include "../sys/gc.h" 11 | typedef struct mspan span; 12 | typedef struct mspanlist spanlist; 13 | typedef uint8 spanclass; 14 | 15 | // 对应golang的 type mspan struct 16 | struct mspan{ 17 | //双向链表 18 | span* next; 19 | span* prev; 20 | //关联了表头,方便debug 21 | spanlist* list; 22 | 23 | uintptr startaddr; 24 | uintptr limit; //指向span已使用空间的末尾 25 | uintptr npages;//记录span里的页数量 ,一个span里有多少页 26 | 27 | 28 | uint8 state; //当前span的状态 29 | uint8 needzero;//是否需要申请前格式化为0 30 | uint8 scavenged; // whether this span has had its pages released to the OS 31 | uint16 allocCount; //记录了申请了多少对象 32 | // sweep generation: h->sweepgen 是全局heap上的 33 | // if sweepgen == h->sweepgen - 2, the span needs sweeping 如果当前sweepgen 等于 全局sweepgen - 2 这个span需要被清除 34 | // if sweepgen == h->sweepgen - 1, the span is currently being swept 正在回收 35 | // if sweepgen == h->sweepgen, the span is swept and ready to use 已经被回收完了 可以使用 36 | // if sweepgen == h->sweepgen + 1, the span was cached before sweep began and is still cached, and needs sweeping 37 | // if sweepgen == h->sweepgen + 3, the span was swept and then cached and is still cached 38 | // h->sweepgen is incremented by 2 after every GC 39 | uint32 sweepgen; 40 | 41 | int64 unusedsince;// first time spotted by gc in Mspanfree state 42 | uint8 spanclass; // size class and noscan (uint8) 43 | 44 | uint16 basemask; // if non-0, elemsize is a power of 2, & this will get object allocation base 45 | uint8 divshift; // for divide by elemsize - divMagic.shift 46 | uint8 divshift2; // for divide by elemsize - divMagic.shift2 47 | uint16 divmul; // for divide by elemsize - divMagic.mul 48 | 49 | uintptr elemsize; // 当前span总共可以分配多少个对象 computed from sizeclass or from npages 50 | 51 | //位图和gc相关 52 | //freeindex是一个标志位,我们知道span里的对象大小都是一样的,所以freeindex指向空闲对象地址的标志位 53 | //如果freeindex == nelem 等于空 则空间满了,sapn不能继续分配了 54 | uintptr freeindex; 55 | uintptr nelems; 56 | //allocBits 是一个位图对象 57 | //如果 n >= freeindex and allocBits[n/8] & (1<<(n%8)) == 0 58 | //说明对象n是空闲可用于分配的内存 59 | //所以对象n从 n*elemsize + (start << pageShift) 地址处开始分配 60 | /* 61 | allocBits 和 gcmarkBits 持有指向 span 的标记和分配位的指针。 指针是 8 字节对齐的。 这些数据保存在三个arenas区域。 62 | free:不再访问的肮脏arenas并且可以重复使用。 63 | next:保存下一个GC周期要用到的信息。 64 | 当前:此 GC 周期中正在使用的信息。 65 | previous:在上一个 GC 周期中使用的信息。 新的 GC 周期从调用 finishsweep_m 开始。 finishsweep_m 将上一个竞技场移动到空闲竞技场,将当前竞技场移动到上一个竞技场,将下一个竞技场移动到当前竞技场。 下一个 arena 被填充,因为跨度请求内存以保存下一个 GC 周期的 gcmarkBits 以及新分配的跨度的 allocBits。 66 | 指针算法是“手动”完成的,而不是使用数组来避免沿关键性能路径进行边界检查。 扫描将释放旧的 allocBits 并将 allocBits 设置为 gcmarkBits。 gcmarkBits 被新清零的内存所取代 67 | */ 68 | uint8* allocBits; 69 | uint8* gcmarkBits; 70 | //8字节 64位来标记每个 每个slot是否被使用的情况 71 | 72 | // Cache of the allocBits at freeindex. allocCache is shifted such that the lowest bit corresponds to the bit freeindex. allocCache holds the complement of allocBits, thus allowing ctz (count trailing zero) to use it directly. allocCache may contain bits beyond s.nelems; the caller must ignore these. freeindex 处的 allocBits 缓存。 allocCache 被移动,使得最低位对应于 freeindex 位。 allocCache 持有 allocBits 的补码,因此允许 ctz(计数尾随零)直接使用它。 allocCache 可能包含超出 s.nelems 的位; 调用者必须忽略这些。 73 | uint64 allocCache; 74 | }; 75 | 76 | struct mspanlist { 77 | span* first; 78 | span* last; 79 | }; 80 | 81 | //span 82 | void span_init(span* s,uintptr base,uintptr npages); 83 | void span_ppbounds(span* s, uintptr* start,uintptr* end); 84 | uintptr span_released(span* s); 85 | uintptr span_scavenge(span* s); 86 | void span_refillAllocCache(span* s,uintptr whichByte); 87 | uintptr span_nextFreeIndex(span* s); 88 | int span_countAlloc(span* s); 89 | void span_layout(span* s ,uintptr* size,uintptr* n,uintptr* total); 90 | bool span_isFree(span* s ,uintptr index); 91 | 92 | //spanlist 93 | void spanlist_remove(spanlist* list,span* s); 94 | void spanlist_insertback(spanlist* list,span* s); 95 | void spanlist_insert(spanlist* list,span* s); 96 | 97 | int8 sizeclass(spanclass sc); 98 | bool noscan(spanclass sc); 99 | spanclass makeSpanClass(spanclass sc,bool noscan); 100 | 101 | 102 | typedef struct { 103 | uint8 shift; 104 | uint8 shift2; 105 | uint8 mul; 106 | uint8 baseMask; 107 | }divMagic; 108 | 109 | extern uint16 class_to_size[_NumSizeClasses]; 110 | extern uint8 class_to_allocnpages[_NumSizeClasses]; 111 | 112 | extern divMagic class_to_divmagic[_NumSizeClasses]; 113 | extern uint8 size_to_class8[smallSizeMax/smallSizeDiv + 1]; 114 | extern uint8 size_to_class128[(_MaxSmallSize-smallSizeMax)/largeSizeDiv + 1]; 115 | extern uint16 class_to_size[_NumSizeClasses]; 116 | extern uint8 class_to_allocnpages[_NumSizeClasses]; 117 | 118 | #endif //GOC_SPAN_H 119 | -------------------------------------------------------------------------------- /gc-golang/span/spanclass.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName spanclass 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 下午 5:08 6 | *@Version 1.0 7 | **/ 8 | #include "span.h" 9 | 10 | divMagic class_to_divmagic[_NumSizeClasses] = {{0, 0, 0, 0}, {3, 0, 1, 65528}, {4, 0, 1, 65520}, {5, 0, 1, 65504}, {4, 9, 171, 0}, {6, 0, 1, 65472}, {4, 10, 205, 0}, {5, 9, 171, 0}, {4, 11, 293, 0}, {7, 0, 1, 65408}, {4, 9, 57, 0}, {5, 10, 205, 0}, {4, 12, 373, 0}, {6, 7, 43, 0}, {4, 13, 631, 0}, {5, 11, 293, 0}, {4, 13, 547, 0}, {8, 0, 1, 65280}, {5, 9, 57, 0}, {6, 9, 103, 0}, {5, 12, 373, 0}, {7, 7, 43, 0}, {5, 10, 79, 0}, {6, 10, 147, 0}, {5, 11, 137, 0}, {9, 0, 1, 65024}, {6, 9, 57, 0}, {7, 6, 13, 0}, {6, 11, 187, 0}, {8, 5, 11, 0}, {7, 8, 37, 0}, {10, 0, 1, 64512}, {7, 9, 57, 0}, {8, 6, 13, 0}, {7, 11, 187, 0}, {9, 5, 11, 0}, {8, 8, 37, 0}, {11, 0, 1, 63488}, {8, 9, 57, 0}, {7, 10, 49, 0}, {10, 5, 11, 0}, {7, 10, 41, 0}, {7, 9, 19, 0}, {12, 0, 1, 61440}, {8, 9, 27, 0}, {8, 10, 49, 0}, {11, 5, 11, 0}, {7, 13, 161, 0}, {7, 13, 155, 0}, {8, 9, 19, 0}, {13, 0, 1, 57344}, {8, 12, 111, 0}, {9, 9, 27, 0}, {11, 6, 13, 0}, {7, 14, 193, 0}, {12, 3, 3, 0}, {8, 13, 155, 0}, {11, 8, 37, 0}, {14, 0, 1, 49152}, {11, 8, 29, 0}, {7, 13, 55, 0}, {12, 5, 7, 0}, {8, 14, 193, 0}, {13, 3, 3, 0}, {7, 14, 77, 0}, {12, 7, 19, 0}, {15, 0, 1, 32768}}; 11 | uint8 size_to_class8[smallSizeMax/smallSizeDiv + 1] = {0, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31}; 12 | uint8 size_to_class128[(_MaxSmallSize-smallSizeMax)/largeSizeDiv + 1] = {31, 32, 33, 34, 35, 36, 36, 37, 37, 38, 38, 39, 39, 39, 40, 40, 40, 41, 42, 42, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, 47, 47, 48, 48, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66}; 13 | uint16 class_to_size[_NumSizeClasses] = {0, 8, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768}; 14 | uint8 class_to_allocnpages[_NumSizeClasses] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 2, 3, 1, 3, 2, 3, 4, 5, 6, 1, 7, 6, 5, 4, 3, 5, 7, 2, 9, 7, 5, 8, 3, 10, 7, 4}; 15 | 16 | /** 17 | * 18 | * @param x 19 | * @return 20 | */ 21 | int bool2int(bool x) 22 | { 23 | // Avoid branches. In the SSA compiler, this compiles to 24 | // exactly what you would want it to. 25 | return (int)((uint8)(*(uint8*)(&x))); 26 | } 27 | 28 | /** 29 | * 30 | * @param sc 31 | * @return 32 | */ 33 | int8 sizeclass(spanclass sc) 34 | { 35 | return sc >> 1; 36 | 37 | } 38 | /** 39 | * 40 | * @param sc 41 | * @return 42 | */ 43 | bool noscan(spanclass sc) 44 | { 45 | return (sc&1) != 0; 46 | 47 | } 48 | /** 49 | * 50 | * @param sc 51 | * @param noscan true代表对象不包含指针 52 | * @return 53 | */ 54 | spanclass makeSpanClass(spanclass sc,bool noscan) 55 | { 56 | return (spanclass)(sc << 1) | (spanclass)bool2int(noscan); // example: sc=3, span class = 3 << 1 | 1 = 7 57 | 58 | } 59 | -------------------------------------------------------------------------------- /gc-golang/sys/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | aux_source_directory(. srcs) 2 | 3 | add_library(sys ${srcs}) 4 | -------------------------------------------------------------------------------- /gc-golang/sys/array.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName array 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 下午 4:16 6 | *@Version 1.0 7 | **/ 8 | #include "array.h" 9 | #include "sysapi.h" 10 | #include "gc.h" 11 | 12 | 13 | int_t array_init(array_t *array, uint_t n, size_t size) 14 | { 15 | array->used = 0; 16 | array->size = size; 17 | array->total = n; 18 | 19 | array->addr = sys_fixalloc(n * size,ptrSize); 20 | if (array->addr == NULL) { 21 | return ERROR; 22 | } 23 | 24 | return OK; 25 | } 26 | 27 | void array_destroy(array_t *a) 28 | { 29 | // free(a); 30 | } 31 | 32 | 33 | void *array_push(array_t *a) 34 | { 35 | void *elt, *new; 36 | size_t size; 37 | 38 | if (a->used == a->total) { 39 | 40 | // 数组满了 41 | size = a->size * a->total; 42 | //直接扩充2倍大小 43 | new = sys_fixalloc(2 * size,ptrSize); 44 | if (new == NULL) { 45 | printf("[arr_pushn] failed to expand memeory\n"); 46 | return NULL; 47 | } 48 | 49 | memcpy(new, a->addr, size); 50 | // free(a->addr); 51 | a->addr = new; 52 | a->total *= 2; 53 | } 54 | 55 | elt = (u_char *) a->addr + a->size * a->used; 56 | a->used++; 57 | return elt; 58 | } 59 | -------------------------------------------------------------------------------- /gc-golang/sys/array.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName array 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 下午 4:16 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_ARRAY_H 9 | #define GOC_ARRAY_H 10 | 11 | #include "stdio.h" 12 | 13 | 14 | typedef unsigned char u_char; 15 | typedef int int_t; 16 | typedef unsigned int uint_t; 17 | typedef unsigned short int u_short; 18 | 19 | 20 | 21 | typedef struct { 22 | void *addr; 23 | uint_t used; 24 | size_t size; 25 | uint_t total; 26 | } array_t; 27 | typedef array_t array; 28 | 29 | int_t array_init(array_t *array, uint_t n, size_t size); 30 | void array_destroy(array_t *a); 31 | void *array_push(array_t *a); 32 | 33 | 34 | 35 | #endif //GOC_ARRAY_H 36 | -------------------------------------------------------------------------------- /gc-golang/sys/defines.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 2021/4/5. 3 | // 4 | 5 | #ifndef GOC_DEFINES_H 6 | #define GOC_DEFINES_H 7 | //common 8 | #define true 1 9 | #define false 0 10 | #define ERROR -1 11 | #define OK 0 12 | //span 13 | #define _MaxSmallSize 32768 14 | #define smallSizeDiv 8 15 | #define smallSizeMax 1024 16 | #define largeSizeDiv 128 17 | #define _NumSizeClasses 67 // 共有67种内存尺寸, size class 从 1 到 66 共 66 个,代码中_NumSizeClasses=67代表了实际使用的 size class 数量,即 67 个,从 0 到 67,size class 0 实际并未使用到 18 | #define _PageShift 13 // 每页大小为8k 19 | #define numSpanClasses (_NumSizeClasses << 1) // 134 20 | #define tinySpanClass (uint8)(tinySizeClass<<1 | 1) 21 | 22 | #define mSpanDead 1 23 | #define mSpanInUse 2 // allocated for garbage collected heap 24 | #define mSpanManual 3 // allocated for manual management (e.g., stack allocator) 25 | #define mSpanFree 4 26 | 27 | #define lock pthread_mutex_lock 28 | #define unlock pthread_mutex_unlock 29 | #define mutex_init(lock) pthread_mutex_init(lock,NULL) 30 | 31 | 32 | // marco 33 | #define pageShift 13 34 | #define pageSize (1 << pageShift) 35 | #define _PageSize (1 << pageShift) 36 | #define PageMask (pageSize - 1) 37 | #define persistentChunkSize (256 << 10) 38 | #define ptrSize (4 << (~(uintptr)0 >> 63)) 39 | #define throw(str) {printf(str);int _a = 10/0;} 40 | #define arenaBaseOffset ((uintptr)(1 << 47)) 41 | #define _64bit (1 << (~(uintptr)0 >> 63) / 2) 42 | #define logHeapArenaBytes ((6+20)*(_64bit) + (2+20)*(1-_64bit)) 43 | #define heapArenaBytes (1 << logHeapArenaBytes) 44 | #define uintptrMask (1<<(8*ptrSize) - 1) 45 | 46 | #define heapAddrBits ((_64bit*(1-GoarchWasm)*(1-GoosAix))*48 + (1-_64bit+GoarchWasm)*(32-(GoarchMips+GoarchMipsle)) + 60*GoosAix) 47 | #define arenaL1Bits (6*(_64bit*GoosWindows) + 12*GoosAix) 48 | #define arenaL2Bits (heapAddrBits - logHeapArenaBytes - arenaL1Bits) 49 | #define arenaBits ( arenaL1Bits + arenaL2Bits) 50 | #define arenaL1Shift arenaL2Bits 51 | #define pagesPerArena (heapArenaBytes / pageSize) 52 | #define heapArenaBitmapBytes (heapArenaBytes / (ptrSize * 8 / 2)) 53 | 54 | #define _TinySize 16 55 | #define _TinySizeClass ((int8)2) 56 | #define maxTinySize _TinySize 57 | #define tinySizeClass _TinySizeClass 58 | #define _MaxSmallSize 32768 59 | #define maxSmallSize _MaxSmallSize 60 | #define maxTinySize _TinySize 61 | 62 | #define wordsPerBitmapByte (8 / 2) // heap words described by one bitmap byte 63 | #define heapBitsShift 1 // shift offset between successive bitPointer or bitScan entries 64 | #define bitPointer (1 << 0) 65 | #define bitScan (1 << 4) 66 | // all scan/pointer bits in a byte 67 | #define bitScanAll (bitScan | bitScan<addr + arr->size*i) 84 | 85 | #endif //GOC_DEFINES_H 86 | -------------------------------------------------------------------------------- /gc-golang/sys/gc.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName gc 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/29 0029 上午 11:39 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_GC_H 9 | #define GOC_GC_H 10 | 11 | #include 12 | #include "platform.h" 13 | #include "defines.h" 14 | #include 15 | 16 | typedef unsigned char uint8; 17 | typedef unsigned short int uint16; 18 | typedef unsigned int uint32; 19 | typedef unsigned int uint; 20 | typedef unsigned long int uint64; 21 | typedef unsigned long int uintptr; 22 | 23 | typedef char int8; 24 | typedef short int int16; 25 | typedef int int32; 26 | typedef long int int64; 27 | typedef long int intptr; 28 | 29 | 30 | typedef uint8 byte; 31 | typedef uint8 bool; 32 | 33 | 34 | /** 35 | * thread mutex 36 | */ 37 | 38 | typedef pthread_mutex_t mutex; 39 | 40 | extern byte deBruijnIdx64[64]; 41 | uintptr round(uintptr n,uintptr a); 42 | uint32 fastrand(); 43 | int ctz64(uint64 x); 44 | 45 | typedef struct 46 | { 47 | uintptr size; 48 | // size of memory prefix holding all pointers 49 | uintptr ptrdata; 50 | //对象类型,有需要包含指针的 有不需要包含指针的 51 | //对于gc来说特别重要,这意味着可能需要递归扫描指针引用 52 | uint8 kind; 53 | }type; 54 | 55 | //gc 56 | //gc处于标记阶段的时候会 置于1 57 | extern uint32 gcBlackenEnabled; 58 | //执行当前gc所处的阶段 59 | //修改时需要保证原子性 60 | extern uint32 gcphase; 61 | extern uint8 oneBitCount[256]; 62 | extern int32 ncpu; 63 | 64 | #define DEBUG(fmt, ...) \ 65 | printf(fmt, ##__VA_ARGS__); \ 66 | printf(" %s() %s:%d\n", __func__, __FILE__, __LINE__) 67 | 68 | #endif //GOC_GC_H 69 | -------------------------------------------------------------------------------- /gc-golang/sys/gpm.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName gpm 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/30 0030 下午 3:42 6 | *@Version 1.0 7 | **/ 8 | #include "gc.h" 9 | #include "gpm.h" 10 | 11 | 12 | __thread g* _g_; 13 | /** 14 | * 多线程安全获取 当前线程变量m 15 | * @return 16 | */ 17 | m* acquirem() 18 | { 19 | 20 | return getg()->m; 21 | } 22 | void releasem(m* m){ 23 | 24 | } 25 | /** 26 | * @return 27 | */ 28 | g* getg() 29 | { 30 | return _g_; 31 | } 32 | -------------------------------------------------------------------------------- /gc-golang/sys/gpm.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | *@ClassName gpm 4 | *@Deacription go 5 | *@Author brewlin 6 | *@Date 2021/3/30 0030 下午 3:44 7 | *@Version 1.0 8 | **/ 9 | #ifndef GOC_GPM_H 10 | #define GOC_GPM_H 11 | 12 | #include "gc.h" 13 | #include "../cache/cache.h" 14 | #include "../malloc/fixalloc.h" 15 | #include "array.h" 16 | #include "../mgc/gc_work.h" 17 | 18 | typedef struct mstack stack; 19 | typedef struct gg g; 20 | typedef struct mm m; 21 | typedef struct pp p; 22 | 23 | struct mstack { 24 | uintptr lo; 25 | uintptr hi; 26 | }; 27 | //协程的主要结构体,通过go() => newproces() new 一个g的结构体 28 | struct gg { 29 | m* m;// 每个协程g所属的线程m current m; offset known to arm liblink 30 | //抢占信号,检测是否被抢占 31 | bool preempt; 32 | //记录了协程栈的信息 33 | stack stk; // offset known to runtime/cgo //这个地方记录了当前协程的栈空间信息 34 | uintptr stackguard0; // offset known to liblink 35 | uintptr stackguard1; // offset known to liblink 36 | //标记辅助 # 37 | // 为了保证用户程序分配内存的速度不会超出后台任务的标记速度,运行时还引入了标记辅助技术, 38 | // 它遵循一条非常简单并且朴实的原则,分配多少内存就需要完成多少标记任务。 39 | // 每一个 Goroutine 都持有 gcAssistBytes 字段,这个字段存储了当前 Goroutine 辅助标记的对象字节数。 40 | // 在并发标记阶段期间,当 Goroutine 调用 runtime.mallocgc 分配新对象时, 41 | // 该函数会检查申请内存的 Goroutine 是否处于入不敷出的状态: 42 | int64 gcAssistBytes; 43 | mutex locks; 44 | }; 45 | 46 | //一个m表示一个ilnux线程,在启动时通过go.maxproces指定最大的线程,目前版本已经没有最大线程的上限了 47 | struct mm { 48 | uintptr pid; 49 | uint mid; 50 | //特殊的协程,每个P固定绑定有一个G,负责配合管理调度 51 | g* g0; 52 | //每个m绑定了一个本地mcache 53 | cache* mcache; 54 | p* p; 55 | //简单的锁计数 56 | mutex locks; 57 | //当前正在运行的G 58 | g* curg; 59 | //如果某个M在进行分配内存工作那么这个线程是不可以被抢占的 60 | int32 mallocing; 61 | //信号处理的协程 62 | g* gsignal; 63 | 64 | uint32 fastrand[2]; 65 | 66 | }; 67 | 68 | 69 | //GPM 中的p 管理者协程队列 70 | struct pp { 71 | //每个p自带一个 固定内存分配 72 | palloc pl; 73 | 74 | //当前P的状态 75 | uint32 status; //当前p的状态 one of pidle/prunning/... 76 | //绑定对应线程M 77 | uintptr m; 78 | 79 | gcWork gcw; 80 | 81 | //每个处理器都标配一个特殊协程,专用于gc阶段的标记工作 82 | uintptr gcBgMarkWorker; 83 | 84 | }; 85 | 86 | 87 | m* acquirem(); 88 | void releasem(m* m); 89 | g* getg(); 90 | extern __thread g* _g_; 91 | extern m* allm[10]; 92 | #endif //GOC_GPM_H 93 | -------------------------------------------------------------------------------- /gc-golang/sys/platform.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName platform 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/31 0031 上午 11:08 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_PLATFORM_H 9 | #define GOC_PLATFORM_H 10 | 11 | #define GoosAix 0 12 | #define GoosAndroid 0 13 | #define GoosDarwin 0 14 | #define GoosDragonfly 0 15 | #define GoosFreebsd 0 16 | #define GoosHurd 0 17 | #define GoosJs 0 18 | #define GoosLinux 1 19 | #define GoosNacl 0 20 | #define GoosNetbsd 0 21 | #define GoosOpenbsd 0 22 | #define GoosPlan9 0 23 | #define GoosSolaris 0 24 | #define GoosWindows 0 25 | #define GoosZos 0 26 | 27 | 28 | #define Goarch386 0 29 | #define GoarchAmd64 1 30 | #define GoarchAmd64p32 0 31 | #define GoarchArm 0 32 | #define GoarchArmbe 0 33 | #define GoarchArm64 0 34 | #define GoarchArm64be 0 35 | #define GoarchPpc64 0 36 | #define GoarchPpc64le 0 37 | #define GoarchMips 0 38 | #define GoarchMipsle 0 39 | #define GoarchMips64 0 40 | #define GoarchMips64le 0 41 | #define GoarchMips64p32 0 42 | #define GoarchMips64p32le 0 43 | #define GoarchPpc 0 44 | #define GoarchRiscv 0 45 | #define GoarchRiscv64 0 46 | #define GoarchS390 0 47 | #define GoarchS390x 0 48 | #define GoarchSparc 0 49 | #define GoarchSparc64 0 50 | #define GoarchWasm 0 51 | 52 | 53 | #define _EINTR 0x4 54 | #define _EAGAIN 0xb 55 | #define _ENOMEM 0xc 56 | 57 | #define _PROT_NONE 0x0 58 | #define _PROT_READ 0x1 59 | #define _PROT_WRITE 0x2 60 | #define _PROT_EXEC 0x4 61 | 62 | #define _MAP_ANON 0x20 63 | #define _MAP_PRIVATE 0x2 64 | #define _MAP_FIXED 0x10 65 | 66 | #define _MADV_DONTNEED 0x4 67 | #define _MADV_FREE 0x8 68 | #define _MADV_HUGEPAGE 0xe 69 | #define _MADV_NOHUGEPAGE 0xf 70 | 71 | #define _SA_RESTART 0x10000000 72 | #define _SA_ONSTACK 0x8000000 73 | #define _SA_RESTORER 0x4000000 74 | #define _SA_SIGINFO 0x4 75 | 76 | #define _SIGHUP 0x1 77 | #define _SIGINT 0x2 78 | #define _SIGQUIT 0x3 79 | #define _SIGILL 0x4 80 | #define _SIGTRAP 0x5 81 | #define _SIGABRT 0x6 82 | #define _SIGBUS 0x7 83 | #define _SIGFPE 0x8 84 | #define _SIGKILL 0x9 85 | #define _SIGUSR1 0xa 86 | #define _SIGSEGV 0xb 87 | #define _SIGUSR2 0xc 88 | #define _SIGPIPE 0xd 89 | #define _SIGALRM 0xe 90 | #define _SIGSTKFLT 0x10 91 | #define _SIGCHLD 0x11 92 | #define _SIGCONT 0x12 93 | #define _SIGSTOP 0x13 94 | #define _SIGTSTP 0x14 95 | #define _SIGTTIN 0x15 96 | #define _SIGTTOU 0x16 97 | #define _SIGURG 0x17 98 | #define _SIGXCPU 0x18 99 | #define _SIGXFSZ 0x19 100 | #define _SIGVTALRM 0x1a 101 | #define _SIGPROF 0x1b 102 | #define _SIGWINCH 0x1c 103 | #define _SIGIO 0x1d 104 | #define _SIGPWR 0x1e 105 | #define _SIGSYS 0x1f 106 | 107 | #define _FPE_INTDIV 0x1 108 | #define _FPE_INTOVF 0x2 109 | #define _FPE_FLTDIV 0x3 110 | #define _FPE_FLTOVF 0x4 111 | #define _FPE_FLTUND 0x5 112 | #define _FPE_FLTRES 0x6 113 | #define _FPE_FLTINV 0x7 114 | #define _FPE_FLTSUB 0x8 115 | 116 | #define _BUS_ADRALN 0x1 117 | #define _BUS_ADRERR 0x2 118 | #define _BUS_OBJERR 0x3 119 | 120 | #define _SEGV_MAPERR 0x1 121 | #define _SEGV_ACCERR 0x2 122 | 123 | #define _ITIMER_REAL 0x0 124 | #define _ITIMER_VIRTUAL 0x1 125 | #define _ITIMER_PROF 0x2 126 | 127 | #define _EPOLLIN 0x1 128 | #define _EPOLLOUT 0x4 129 | #define _EPOLLERR 0x8 130 | #define _EPOLLHUP 0x10 131 | #define _EPOLLRDHUP 0x2000 132 | #define _EPOLLET 0x80000000 133 | #define _EPOLL_CLOEXEC 0x80000 134 | #define _EPOLL_CTL_ADD 0x1 135 | #define _EPOLL_CTL_DEL 0x2 136 | #define _EPOLL_CTL_MOD 0x3 137 | 138 | #define _AF_UNIX 0x1 139 | #define _F_SETFL 0x4 140 | #define _SOCK_DGRAM 0x2 141 | 142 | 143 | #endif //GOC_PLATFORM_H 144 | -------------------------------------------------------------------------------- /gc-golang/sys/sys.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 2021/4/17. 3 | // 4 | #include "gc.h" 5 | #include "gpm.h" 6 | 7 | 8 | byte deBruijnIdx64[64] = { 9 | 0, 1, 2, 7, 3, 13, 8, 19, 10 | 4, 25, 14, 28, 9, 34, 20, 40, 11 | 5, 17, 26, 38, 15, 46, 29, 48, 12 | 10, 31, 35, 54, 21, 50, 41, 57, 13 | 63, 6, 12, 18, 24, 27, 33, 39, 14 | 16, 37, 45, 47, 30, 53, 49, 56, 15 | 62, 11, 23, 32, 36, 44, 52, 55, 16 | 61, 22, 43, 51, 60, 42, 59, 58, 17 | }; 18 | 19 | uint32 gcBlackenEnabled; 20 | uint32 gcphase; 21 | int32 ncpu; 22 | 23 | m* allm[10]; 24 | 25 | 26 | // inline function 27 | uintptr round(uintptr n,uintptr a) 28 | { 29 | return (n + a - 1) &~ (a - 1); 30 | } 31 | uint32 fastrand() 32 | { 33 | m* mp = getg()->m; 34 | uint32 s1,s0; 35 | s1 = mp->fastrand[0]; 36 | s0 = mp->fastrand[1]; 37 | s1 ^= s1 << 17; 38 | s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16; 39 | mp->fastrand[0] = s0; 40 | mp->fastrand[1] = s1; 41 | return s0 + s1; 42 | } 43 | int ctz64(uint64 x) 44 | { 45 | x &= -x; // isolate low-order bit 46 | int y = x * deBruijn64 >> 58; // extract part of deBruijn sequence 47 | int i = (int)(deBruijnIdx64[y]); // convert to bit index 48 | int z = (int)((x - 1) >> 57 & 64); // adjustment if zero 49 | return i + z; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /gc-golang/sys/sysapi.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName sysapi 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/3/30 0030 下午 2:50 6 | *@Version 1.0 7 | **/ 8 | #ifndef GOC_SYSAPI_H 9 | #define GOC_SYSAPI_H 10 | 11 | #include "gc.h" 12 | 13 | #define AMD64 1 14 | #define ARM 2 15 | #define ARM64 3 16 | #define I386 4 17 | #define MIPS 5 18 | #define MIPS64 6 19 | #define PPC64 7 20 | #define S390X 8 21 | #define WASM 9 22 | 23 | #define ArchFamily AMD64 24 | #define BigEndian false 25 | #define DefaultPhysPageSize 4096 26 | #define PCQuantum 1 27 | #define Int64Align 8 28 | #define HugePageSize (1 << 21) 29 | #define MinFrameSize 0 30 | 31 | 32 | void* sys_alloc(uintptr n); 33 | void sys_unused(void* v,uintptr n); 34 | void sys_used(void* v,uintptr n); 35 | void sys_free(void* v,uintptr n); 36 | void sys_fault(void* v,uintptr n); 37 | void* sys_reserve(void* v,uintptr n); 38 | void* sys_reserveAligned(void* v,uintptr* ssize,uintptr align); 39 | void sys_map(void* v,uintptr n); 40 | void* sys_fixalloc(uintptr size,uintptr align); 41 | #endif //GOC_SYSAPI_H 42 | -------------------------------------------------------------------------------- /gc-golang/test.c: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName gc 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2021/4/1 0001 下午 3:39 6 | *@Version 1.0 7 | **/ 8 | #include "sys/gc.h" 9 | #include 10 | #include "malloc/malloc.h" 11 | #include 12 | #include "sys/gpm.h" 13 | #include "heap/heap.h" 14 | #include "proc/proc.h" 15 | #include "sys/defines.h" 16 | #include "mgc/gc.h" 17 | #include "mgc/root.h" 18 | 19 | 20 | void initm(int _mid) 21 | { 22 | stk_start = get_sp(); 23 | regsig(); 24 | _g_ = malloc(sizeof(g)); 25 | _g_->m = malloc(sizeof(m)); 26 | _g_->m->mallocing = 0; 27 | _g_->m->mcache = allocmcache(); 28 | mid = _mid; 29 | _g_->m->mid = _mid; 30 | _g_->m->pid = pthread_self(); 31 | 32 | } 33 | void* alloc(void* arg){ 34 | //每个线程初始化本地tls 35 | initm(arg); 36 | allm[(int)arg] = getg()->m; 37 | while(gcphase != _GCoff){} 38 | 39 | 40 | int n = 100; 41 | //构造一个需要gc扫描的内存 42 | type typ; 43 | typ.kind = 1; 44 | int* arr = mallocgc(n* sizeof(int),&typ,true); 45 | for(int i = 0; i< n ;i ++){ 46 | //干扰信息 47 | int interfere = rand()%90; 48 | mallocgc(interfere,NULL,true); 49 | arr[i] = i; 50 | } 51 | for(int i = 0; i < n ; i ++){ 52 | if(arr[i] != i) throw("mem error!") 53 | } 54 | 55 | 56 | } 57 | void osinit() 58 | { 59 | ncpu = (int32)(sysconf(_SC_NPROCESSORS_ONLN)); 60 | physPageSize = sysconf(_SC_PAGESIZE); // 出于通用性的考虑, 物理页大小获取的方式是通过 POSIX sysctl 这个系统调用进行获取 61 | gcphase = _GCoff; 62 | gcBlackenEnabled = false; 63 | //初始化全局堆, 对应 func mallocinit() 64 | mallocinit(); 65 | //TODO: 开启多线程进行并发gc需要调度器参与,目前只支持单线程gc,但支持多线程分配 66 | pthread_t tid; 67 | pthread_create(&tid,NULL,alloc,0); 68 | pthread_join(tid,NULL); 69 | } 70 | void main(){ 71 | DEBUG("start"); 72 | osinit(); // 对应golang src/runtime/os_linux.go func osinit() osinit 完成对 CPU 核心数的获取,因为这与调度器有关, 运行时最为重要的两个系统级参数:CPU 核心数与内存物理页大小 73 | DEBUG("end"); 74 | } 75 | -------------------------------------------------------------------------------- /gc-python/Array.c: -------------------------------------------------------------------------------- 1 | #include "Array.h" 2 | #include "gc.h" 3 | 4 | int_t array_init(array_t *array, uint_t n, size_t size) 5 | { 6 | array->used = 0; 7 | array->size = size; 8 | array->total = n; 9 | 10 | array->addr = gc_malloc(n * size); 11 | if (array->addr == NULL) { 12 | return ERROR; 13 | } 14 | 15 | return OK; 16 | } 17 | array_t *array_create(uint_t n, size_t size) 18 | { 19 | array_t *a; 20 | 21 | a = gc_malloc(sizeof(array_t)); 22 | if (a == NULL) { 23 | printf("[arr_create] failed to create\n"); 24 | return NULL; 25 | } 26 | 27 | if (array_init(a, n, size) != OK) { 28 | printf("[arr_init] failed to init\n"); 29 | return NULL; 30 | } 31 | 32 | return a; 33 | } 34 | 35 | 36 | void array_destroy(array_t *a) 37 | { 38 | //TODO:释放array 39 | gc_free(a); 40 | } 41 | 42 | 43 | void *array_push(array_t *a) 44 | { 45 | void *elt, *new; 46 | size_t size; 47 | 48 | if (a->used == a->total) { 49 | 50 | // 数组满了 51 | size = a->size * a->total; 52 | //直接扩充2倍大小 53 | new = gc_malloc(2 * size); 54 | if (new == NULL) { 55 | printf("[arr_pushn] failed to expand memeory\n"); 56 | return NULL; 57 | } 58 | memcpy(new, a->addr, size); 59 | //手动释放之前决定不会用到的数组 降低gc压力 60 | gc_free(a->addr); 61 | a->addr = new; 62 | a->total *= 2; 63 | } 64 | 65 | elt = (u_char *) a->addr + a->size * a->used; 66 | a->used++; 67 | return elt; 68 | } 69 | 70 | 71 | void * 72 | array_push_n(array_t *a, uint_t n) 73 | { 74 | void *elt, *new; 75 | size_t size; 76 | uint_t total; 77 | 78 | size = n * a->size; 79 | 80 | if (a->used + n > a->total) { 81 | //数组满了 82 | 83 | total = 2 * ((n >= a->total) ? n : a->total); 84 | 85 | new = gc_malloc(total * a->size); 86 | if (new == NULL) { 87 | printf("[arr_pushn] failed to expand memeory"); 88 | return NULL; 89 | } 90 | memcpy(new, a->addr, a->used * a->size); 91 | //手动释放拷贝前的数组降低gc压力 92 | gc_free(a->addr); 93 | a->addr = new; 94 | a->total = total; 95 | } 96 | elt = (u_char *) a->addr + a->size * a->used; 97 | a->used += n; 98 | 99 | return elt; 100 | } 101 | -------------------------------------------------------------------------------- /gc-python/Array.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _ARRAY_H_INCLUDED_ 3 | #define _ARRAY_H_INCLUDED_ 4 | 5 | #include 6 | #define TRUE 1 7 | #define FALSE 0 8 | #define OK 0 9 | #define ERROR -1 10 | typedef int int_t; 11 | #define ushort_t u_short 12 | #define uint_t int 13 | #define ARRAY_SIZE 8 14 | 15 | typedef struct { 16 | void *addr; 17 | uint_t used; 18 | size_t size; 19 | uint_t total; 20 | } array_t; 21 | 22 | 23 | 24 | array_t *array_create(uint_t n, size_t size); 25 | void array_destroy(array_t *a); 26 | void *array_push(array_t *a); 27 | void *array_push_n(array_t *a, uint_t n); 28 | 29 | #endif /* _ARRAY_H_INCLUDED_ */ 30 | -------------------------------------------------------------------------------- /gc-python/Hugmem.c: -------------------------------------------------------------------------------- 1 | #include "Hugmem.h" 2 | #include "gc.h" 3 | 4 | 5 | void push(List* list,void* v,int size) 6 | { 7 | ListNode* node = malloc(sizeof(ListNode)); 8 | node->value = v; 9 | node->size = size; 10 | node->next = NULL; 11 | if(list->root == NULL){ 12 | list->root = node; 13 | return; 14 | } 15 | node->next = list->root; 16 | list->root = node; 17 | } 18 | void mark(List* list,void* v){ 19 | ListNode* cur = list->root; 20 | while(cur != NULL){ 21 | if(cur->value == v){ 22 | Header* hdr = (Header*)((void*)v - 8); 23 | FL_SET(hdr->flags,FL_MARK); 24 | for (void *p = v; p < (v + cur->size -8); p += 1){ 25 | gc_mark(*(void**)p); 26 | } 27 | 28 | return; 29 | } 30 | cur = cur->next; 31 | } 32 | } 33 | void del(List* list,void *v) 34 | { 35 | if(!list->root || !v)return; 36 | 37 | ListNode* tmp = list->root; 38 | if(tmp->value == v){ 39 | list->root = tmp->next; 40 | return; 41 | } 42 | while(tmp != NULL && tmp->next != NULL){ 43 | if(tmp->next->value == v){ 44 | tmp->next = tmp->next->next; 45 | return; 46 | } 47 | tmp = tmp->next; 48 | } 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /gc-python/Hugmem.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef GC_LIST_H 3 | #define GC_LIST_H 4 | 5 | #include 6 | #include 7 | typedef struct link_list 8 | { 9 | void* value; 10 | int size; 11 | struct link_list *next; 12 | }ListNode; 13 | 14 | typedef struct list{ 15 | ListNode *root; 16 | }List; 17 | void push(List* list,void* v,int size); 18 | void mark(List* list,void* v); 19 | void del(List* list,void* v); 20 | 21 | #endif //GC_LIST_H 22 | -------------------------------------------------------------------------------- /gc-python/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) $(wildcard *.s) 3 | BIN = gc 4 | 5 | run: clean gcrun 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | gcrun: $(SRCS) 11 | $(CC) -g -o gc $(SRCS) 12 | -------------------------------------------------------------------------------- /gc-python/README.md: -------------------------------------------------------------------------------- 1 | 无侵入式GC、保守式GC、基于python的分配器 -------------------------------------------------------------------------------- /gc-python/gc.h: -------------------------------------------------------------------------------- 1 | #ifndef PRO_LEARN_GC_H 2 | #define PRO_LEARN_GC_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "Hugmem.h" 15 | 16 | #define CO_SSIZE_T_MAX INT_MAX 17 | #define CO_SSIZE_T_MIN INT_MIN 18 | typedef unsigned long Py_uintptr_t; 19 | typedef long Py_intptr_t; 20 | #define PyMem_MALLOC malloc 21 | #define Py_FatalError(str) printf(str);exit(-1) 22 | #define PyMem_FREE free 23 | /* 24 | * 内存分配器 25 | * 分配策略: 26 | * 1. 小内存就用当前的分配器 分配block 27 | * 2. 超过256bytes就进行系统malloc调用 28 | * 29 | * 3. 下面有一个循环链表,多链表,加快内存分配 30 | * ---------------------------------------------------------------- 31 | * 1-8 8 0 32 | * 9-16 16 1 33 | * 17-24 24 2 34 | * 25-32 32 3 35 | * 33-40 40 4 36 | * 41-48 48 5 37 | * 49-56 56 6 38 | * 57-64 64 7 39 | * 65-72 72 8 40 | * ... ... ... 41 | * 241-248 248 30 42 | * 249-256 256 31 43 | */ 44 | 45 | #define ALIGNMENT 8 //进行8字节内存对齐 46 | #define ALIGNMENT_SHIFT 3 47 | #define ALIGNMENT_MASK (ALIGNMENT - 1) 48 | 49 | //将索引转换为字节大小 (i + 1) * 8 50 | #define INDEX2SIZE(I) (((uint)(I) + 1) << ALIGNMENT_SHIFT) 51 | 52 | 53 | #define SMALL_REQUEST_THRESHOLD 256 54 | #define NB_SMALL_SIZE_CLASSES (SMALL_REQUEST_THRESHOLD / ALIGNMENT) 55 | 56 | /* 57 | * 可以通过 getpagesize() 获取系统页大小 58 | * 为了计算不那么复杂,默认为4k大小,已经足够适配各个平台了 59 | */ 60 | #define SYSTEM_PAGE_SIZE (4 * 1024) 61 | #define SYSTEM_PAGE_SIZE_MASK (SYSTEM_PAGE_SIZE - 1) 62 | 63 | //1级内存区 默认的arena 为256k的大内存块 64 | #define ARENA_SIZE (256 << 10) /* 256KB */ 65 | //2级内存区 默认的pool 为4k的大小 66 | #define POOL_SIZE SYSTEM_PAGE_SIZE /* must be 2^N */ 67 | #define POOL_SIZE_MASK SYSTEM_PAGE_SIZE_MASK 68 | 69 | // 重新定义基础类型 70 | #undef uchar 71 | #define uchar unsigned char /* assuming == 8 bits */ 72 | 73 | #undef uint 74 | #define uint unsigned int /* assuming >= 16 bits */ 75 | 76 | #undef ulong 77 | #define ulong unsigned long /* assuming >= 32 bits */ 78 | 79 | #undef uptr 80 | #define uptr Py_uintptr_t 81 | 82 | //block定义为一个uchar 83 | typedef uchar block; 84 | 85 | struct pool_header { 86 | union { 87 | block *_padding; 88 | uint count; 89 | } ref; /* number of allocated blocks */ 90 | block *freeblock; //指向空闲的block链表 91 | struct pool_header *nextpool; /* next pool of this size class */ 92 | struct pool_header *prevpool; /* previous pool "" */ 93 | uint arenaindex; /* index into arenas of base adr */ 94 | uint szidx; /* block size class index */ 95 | uint nextoffset; /* bytes to virgin block */ 96 | uint maxnextoffset; /* largest valid nextoffset */ 97 | }; 98 | typedef struct header{ 99 | size_t flags; 100 | void* data; 101 | }Header; 102 | 103 | typedef struct pool_header *poolp; 104 | 105 | //记录保存了arenas1级内存区 106 | struct arena_object { 107 | //从malloc分配保存的直接地址 108 | uptr address; 109 | 110 | //对address进行4k内存对齐后的首地址 不在变动 111 | block* first_address; 112 | 113 | //对address进行4k内存对齐后的首地址 114 | block* pool_address; 115 | //在arena上 可用的内存池pool的数量,usable_areanas 中会根据这个来进行 116 | //排序,使得分配的时候速度会更快 117 | uint nfreepools; 118 | 119 | //总pool个数,因为进行4k内存对齐后个数可能不是 256/4 120 | uint ntotalpools; 121 | 122 | //单链表指向可用的pools 123 | struct pool_header* freepools; 124 | 125 | //所有分配的arena被关联为一个双向链表 126 | struct arena_object* nextarena; 127 | struct arena_object* prevarena; 128 | }; 129 | extern struct arena_object* arenas; 130 | #undef ROUNDUP 131 | //进行8字节对齐 进行8字节4舍5入 132 | #define ROUNDUP(x) (((x) + ALIGNMENT_MASK) & ~ALIGNMENT_MASK) 133 | //计算header头的对齐大小 134 | #define POOL_OVERHEAD ROUNDUP(sizeof(struct pool_header)) 135 | 136 | #define DUMMY_SIZE_IDX 0xffff /* size class of newly cached pools */ 137 | 138 | //计算出i字节能够分配多少个block 139 | #define NUMBLOCKS(I) ((uint)(POOL_SIZE - POOL_OVERHEAD) / INDEX2SIZE(I)) 140 | 141 | extern uint maxarenas; 142 | 143 | //多空闲链表,加开内存分配速度 基本上O(1) 144 | #define PTA(x) ((poolp )((uchar *)&(usedpools[2*(x)]) - 2*sizeof(block *))) 145 | #define PT(x) PTA(x), PTA(x) 146 | //小技巧: 在pool内分配的内存都可以通过下面立马计算出 pool_header的地址,非常高效 147 | #define POOL_ADDR(P) ((poolp)((uptr)(P) & ~(uptr)POOL_SIZE_MASK)) 148 | //判断指针是否合法 149 | #define Py_ADDRESS_IN_RANGE(P, POOL) \ 150 | ((POOL)->arenaindex < maxarenas && \ 151 | (uptr)(P) - arenas[(POOL)->arenaindex].address < (uptr)ARENA_SIZE && \ 152 | arenas[(POOL)->arenaindex].address != 0) 153 | 154 | 155 | /* flags */ 156 | #define FL_ALLOC 0x1 157 | #define FL_MARK 0x2 158 | #define FL_SET(x, f) (x |= f) 159 | #define FL_UNSET(x, f) (x &= ~(f)) 160 | #define FL_TEST(x, f) (x & f) 161 | #define IS_MARKED(x) (FL_TEST(x, FL_ALLOC) && FL_TEST(x, FL_MARK)) 162 | 163 | 164 | #define NOT_STACK 10 165 | #define TRUE 1 166 | extern void* sp_start; 167 | 168 | //执行内存分配逻辑 169 | void* Malloc(size_t nbytes); 170 | void* Realloc(void *p, size_t nbytes); 171 | void Free(void *p); 172 | void gc(); 173 | void gc_init(); 174 | void* gc_malloc(size_t nbytes); 175 | void* gc_realloc(void *p, size_t nbytes); 176 | void gc_free(void *p); 177 | int gc_mark(void * ptr); 178 | struct arena_object* new_arena(void); 179 | 180 | extern List Hugmem; 181 | 182 | 183 | 184 | 185 | #endif //PRO_LEARN_GC_H 186 | -------------------------------------------------------------------------------- /gc-python/gc_test.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | #include "root.h" 3 | #include "Array.h" 4 | #include 5 | #include "help.h" 6 | 7 | typedef struct obj 8 | { 9 | int a; 10 | int b; 11 | struct obj *next; 12 | }Obj; 13 | Obj *t; 14 | int __failed_tests = 0; 15 | int __test_num = 0; 16 | 17 | void array_test() 18 | { 19 | array_t* arr = array_create(8,sizeof(Obj*)); 20 | assert_s("[arr_create]",arr != NULL) 21 | for (int i = 0; i < 1000; ++i) { 22 | Obj** tmp = array_push(arr); 23 | *tmp = gc_malloc(sizeof(Obj)); 24 | (*tmp)->a = i; 25 | (*tmp)->b = i; 26 | } 27 | Obj** index = arr->addr; 28 | gc(); 29 | for(int i = 0 ; i < 1000; ++i){ 30 | assert(index[i]->a == i); 31 | assert(index[i]->b == i); 32 | } 33 | gc(); 34 | 35 | } 36 | int test_speed() 37 | { 38 | clock_t start, finish; 39 | double duration; 40 | start = clock(); 41 | for (int i = 0; i < 1000000; ++i) { 42 | int size = rand()%90; 43 | void* p = gc_malloc(size); 44 | *(int*)p = size; 45 | } 46 | finish = clock(); 47 | duration = (double)(finish - start) / CLOCKS_PER_SEC; 48 | assert_s("[speed-test]",duration) 49 | printf( "%f seconds\n", duration ); 50 | return 0; 51 | } 52 | 53 | void test_logic(){ 54 | Obj* p1 = gc_malloc(sizeof(Obj)); 55 | p1->a = 10; 56 | p1->b = 20; 57 | gc(); 58 | assert_s("[logic] test stack",p1->a == 10); 59 | assert_s("[logic] test p->b == 20",p1->b == 20); 60 | p1->next = gc_malloc(sizeof(Obj)); 61 | assert_s("[logic] test p1->next",p1->next); 62 | p1->next->a = 22; 63 | p1->next->b = 33; 64 | gc(); 65 | assert_s("[logic] p1->next->a",p1->next->a == 22); 66 | assert_s("[logic] p1->next->b",p1->next->b == 33); 67 | } 68 | void main(){ 69 | sp_start = get_sp(); 70 | array_test(); 71 | test_speed(); 72 | test_logic(); 73 | test_report(); 74 | } 75 | -------------------------------------------------------------------------------- /gc-python/help.h: -------------------------------------------------------------------------------- 1 | /* 2 | * test_cond("Check if 1 == 1", 1==1) 3 | * test_cond("Check if 5 > 10", 5 > 10) 4 | * test_report() 5 | */ 6 | 7 | 8 | #ifndef __TESTHELP_H 9 | #define __TESTHELP_H 10 | 11 | extern int __failed_tests; 12 | extern int __test_num; 13 | 14 | #define assert_s(descr,_c) do { \ 15 | __test_num++; printf("%d - %s: ", __test_num, descr); \ 16 | if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \ 17 | } while(0); 18 | #define test_report() do { \ 19 | printf("%d tests, %d passed, %d failed\n", __test_num, \ 20 | __test_num-__failed_tests, __failed_tests); \ 21 | if (__failed_tests) { \ 22 | printf("=== WARNING === We have failed tests here...\n"); \ 23 | exit(1); \ 24 | } \ 25 | } while(0); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /gc-python/malloc/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) 13 | 14 | -------------------------------------------------------------------------------- /gc-python/malloc/obmalloc.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName gc 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2020/11/6 0006 上午 9:10 6 | *@Version 1.0 7 | **/ 8 | #ifndef PRO_LEARN_GC_H 9 | #define PRO_LEARN_GC_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #define PY_SSIZE_T_MAX INT_MAX 23 | #define PY_SSIZE_T_MIN INT_MIN 24 | typedef unsigned long Py_uintptr_t; 25 | typedef long Py_intptr_t; 26 | #define PyMem_MALLOC malloc 27 | #define Py_FatalError(str) printf(str);exit(-1) 28 | #define PyMem_FREE free 29 | /* 30 | * python的内存分配器 31 | * 分配策略: 32 | * 1. 小内存就用当前的分配器 分配block 33 | * 2. 超过256bytes就进行系统malloc调用 34 | * 35 | * 3. 下面有一个循环链表,多链表,加快内存分配 36 | * ---------------------------------------------------------------- 37 | * 1-8 8 0 38 | * 9-16 16 1 39 | * 17-24 24 2 40 | * 25-32 32 3 41 | * 33-40 40 4 42 | * 41-48 48 5 43 | * 49-56 56 6 44 | * 57-64 64 7 45 | * 65-72 72 8 46 | * ... ... ... 47 | * 241-248 248 30 48 | * 249-256 256 31 49 | */ 50 | 51 | #define ALIGNMENT 8 //进行8字节内存对齐 52 | #define ALIGNMENT_SHIFT 3 53 | #define ALIGNMENT_MASK (ALIGNMENT - 1) 54 | 55 | //将索引转换为字节大小 (i + 1) * 8 56 | #define INDEX2SIZE(I) (((uint)(I) + 1) << ALIGNMENT_SHIFT) 57 | 58 | 59 | #define SMALL_REQUEST_THRESHOLD 256 60 | #define NB_SMALL_SIZE_CLASSES (SMALL_REQUEST_THRESHOLD / ALIGNMENT) 61 | 62 | /* 63 | * 可以通过 getpagesize() 获取系统页大小 64 | * 为了计算不那么复杂,python默认为4k大小,已经足够适配各个平台了 65 | */ 66 | #define SYSTEM_PAGE_SIZE (4 * 1024) 67 | #define SYSTEM_PAGE_SIZE_MASK (SYSTEM_PAGE_SIZE - 1) 68 | 69 | //1级内存区 默认的arena 为256k的大内存块 70 | #define ARENA_SIZE (256 << 10) /* 256KB */ 71 | //2级内存区 默认的pool 为4k的大小 72 | #define POOL_SIZE SYSTEM_PAGE_SIZE /* must be 2^N */ 73 | #define POOL_SIZE_MASK SYSTEM_PAGE_SIZE_MASK 74 | 75 | // 重新定义基础类型 76 | #undef uchar 77 | #define uchar unsigned char /* assuming == 8 bits */ 78 | 79 | #undef uint 80 | #define uint unsigned int /* assuming >= 16 bits */ 81 | 82 | #undef ulong 83 | #define ulong unsigned long /* assuming >= 32 bits */ 84 | 85 | #undef uptr 86 | #define uptr Py_uintptr_t 87 | 88 | //block定义为一个uchar 89 | typedef uchar block; 90 | 91 | struct pool_header { 92 | union { 93 | block *_padding; 94 | uint count; 95 | } ref; /* number of allocated blocks */ 96 | block *freeblock; //指向空闲的block链表 97 | struct pool_header *nextpool; /* next pool of this size class */ 98 | struct pool_header *prevpool; /* previous pool "" */ 99 | uint arenaindex; /* index into arenas of base adr */ 100 | uint szidx; /* block size class index */ 101 | uint nextoffset; /* bytes to virgin block */ 102 | uint maxnextoffset; /* largest valid nextoffset */ 103 | }; 104 | 105 | typedef struct pool_header *poolp; 106 | 107 | //记录保存了arenas1级内存区 108 | struct arena_object { 109 | //从malloc分配保存的直接地址 110 | uptr address; 111 | 112 | //对address进行4k内存对齐后的首地址 113 | block* pool_address; 114 | //在arena上 可用的内存池pool的数量,usable_areanas 中会根据这个来进行 115 | //排序,使得分配的时候速度会更快 116 | uint nfreepools; 117 | 118 | //总pool个数,因为进行4k内存对齐后个数可能不是 256/4 119 | uint ntotalpools; 120 | 121 | //单链表指向可用的pools 122 | struct pool_header* freepools; 123 | 124 | //所有分配的arena被关联为一个双向链表 125 | struct arena_object* nextarena; 126 | struct arena_object* prevarena; 127 | }; 128 | 129 | #undef ROUNDUP 130 | //进行8字节对齐 进行8字节4舍5入 131 | #define ROUNDUP(x) (((x) + ALIGNMENT_MASK) & ~ALIGNMENT_MASK) 132 | //计算header头的对齐大小 133 | #define POOL_OVERHEAD ROUNDUP(sizeof(struct pool_header)) 134 | 135 | #define DUMMY_SIZE_IDX 0xffff /* size class of newly cached pools */ 136 | 137 | //计算出i字节能够分配多少个block 138 | #define NUMBLOCKS(I) ((uint)(POOL_SIZE - POOL_OVERHEAD) / INDEX2SIZE(I)) 139 | 140 | 141 | 142 | 143 | //执行内存分配逻辑 144 | void * PyObject_Malloc(size_t nbytes); 145 | void* PyObject_Realloc(void *p, size_t nbytes); 146 | struct arena_object* new_arena(void); 147 | void PyObject_Free(void *p); 148 | 149 | 150 | 151 | 152 | 153 | #endif //PRO_LEARN_GC_H 154 | -------------------------------------------------------------------------------- /gc-python/malloc/python.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName python 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2020/11/6 0006 下午 2:35 6 | *@Version 1.0 7 | **/ 8 | #ifndef GC_LEARNING_PYTHON_H 9 | #define GC_LEARNING_PYTHON_H 10 | 11 | #include "obmalloc.h" 12 | #endif //GC_LEARNING_PYTHON_H 13 | -------------------------------------------------------------------------------- /gc-python/malloc/test.c: -------------------------------------------------------------------------------- 1 | #include "python.h" 2 | 3 | int main() 4 | { 5 | 6 | clock_t start, finish; 7 | double duration; 8 | start = clock(); 9 | for (int i = 0; i < 100000; ++i) { 10 | int size = rand()%90; 11 | void* p = PyObject_Malloc(size); 12 | // PyObject_Free(p); 13 | } 14 | finish = clock(); 15 | duration = (double)(finish - start) / CLOCKS_PER_SEC; 16 | printf( "%f seconds\n", duration ); 17 | return 0; 18 | } -------------------------------------------------------------------------------- /gc-python/root.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName root 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2020/11/6 0006 下午 3:18 6 | *@Version 1.0 7 | **/ 8 | #ifndef GC_LEARNING_ROOT_H 9 | #define GC_LEARNING_ROOT_H 10 | 11 | void* get_sp()asm("get_sp"); 12 | void* get_bp()asm("get_bp"); 13 | void* get_di()asm("get_di"); 14 | void* get_si()asm("get_si"); 15 | void* get_dx()asm("get_dx"); 16 | void* get_cx()asm("get_cx"); 17 | void* get_r8()asm("get_r8"); 18 | void* get_r9()asm("get_r9"); 19 | void* get_bp()asm("get_bp"); 20 | void* get_ax()asm("get_ax"); 21 | void* get_bx()asm("get_bx"); 22 | 23 | #endif //GC_LEARNING_ROOT_H 24 | -------------------------------------------------------------------------------- /gc-python/root.s: -------------------------------------------------------------------------------- 1 | .text 2 | 3 | .globl get_sp 4 | get_sp: 5 | movq %rsp,%rax 6 | ret 7 | 8 | .globl get_bp 9 | get_bp: 10 | movq %rbp,%rax 11 | ret 12 | 13 | .globl get_di 14 | get_di: 15 | movq %rdi,%rax 16 | ret 17 | 18 | .globl get_si 19 | get_si: 20 | movq %rsi,%rax 21 | ret 22 | 23 | .globl get_dx 24 | get_dx: 25 | movq %rdx,%rax 26 | ret 27 | 28 | .globl get_cx 29 | get_cx: 30 | movq %rcx,%rax 31 | ret 32 | 33 | .globl get_r8 34 | get_r8: 35 | movq %r8,%rax 36 | ret 37 | 38 | .globl get_r9 39 | get_r9: 40 | movq %r9,%rax 41 | ret 42 | 43 | .globl get_ax 44 | get_ax: 45 | movq %rax,%rax 46 | ret 47 | 48 | .globl get_bx 49 | get_bx: 50 | movq %rbx,%rax 51 | ret 52 | -------------------------------------------------------------------------------- /gc-python/sweep.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | #include "root.h" 3 | #include "Hugmem.h" 4 | 5 | /** 6 | * 对该对象进行标记 7 | * 并进行子对象标记 8 | * @param ptr 9 | */ 10 | int gc_mark(void * ptr) 11 | { 12 | if (ptr == NULL)return TRUE; 13 | 14 | poolp pool; 15 | uint size; 16 | pool = POOL_ADDR(ptr); 17 | if((uptr)pool < arenas[0].address || (uptr)pool > (arenas[0].address + ARENA_SIZE)) { 18 | // mark(&Hugmem,ptr); 19 | return NOT_STACK; 20 | } 21 | if (!Py_ADDRESS_IN_RANGE(ptr, pool)) { 22 | // mark(&Hugmem,ptr); 23 | return NOT_STACK; 24 | // return; 25 | } 26 | 27 | size = INDEX2SIZE(pool->szidx); 28 | 29 | Header *hdr = ptr - 8; 30 | if (!FL_TEST(hdr->flags, FL_ALLOC)) { 31 | // printf("flag not set alloc\n"); 32 | return TRUE; 33 | } 34 | if (FL_TEST(hdr->flags, FL_MARK)) { 35 | //printf("flag not set mark\n"); 36 | return TRUE; 37 | } 38 | //printf("marking %p:%d ",ptr,*(int*)ptr); 39 | /* marking */ 40 | FL_SET(hdr->flags, FL_MARK); 41 | 42 | //进行child 节点递归 标记 43 | for (void* p = ptr; p < (ptr + size -8); p++) { 44 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 45 | gc_mark(*(void **)p); 46 | } 47 | return TRUE; 48 | } 49 | /** 50 | * 清除 未标记内存 进行回收利用 51 | */ 52 | void gc_sweep(void) 53 | { 54 | //遍历areans_object 55 | struct arena_object* area = &arenas[0]; 56 | poolp pool; 57 | uint size = 0; 58 | 59 | //从 first_pools - pool_adress 之间遍历 60 | for (void* p = area->first_address; p < (void*)area->pool_address; p += POOL_SIZE) 61 | { 62 | pool = (poolp)p; 63 | size = INDEX2SIZE(pool->szidx); 64 | void* start_addr = p + POOL_OVERHEAD; 65 | void* end_addr = p + POOL_SIZE; 66 | for (void *pp = start_addr; pp < end_addr; pp += size) 67 | { 68 | Header *obj = (Header*)pp; 69 | //查看该堆是否已经被使用 70 | if (FL_TEST(obj->flags, FL_ALLOC)) { 71 | //查看该堆是否被标记过 72 | if (FL_TEST(obj->flags, FL_MARK)) { 73 | // DEBUG(printf("解除标记 : %p\n", p)); 74 | // printf("解除标记 : %p\n", p); 75 | FL_UNSET(obj->flags, FL_MARK); 76 | }else { 77 | // DEBUG(printf("清除回收 :\n")); 78 | //printf("清除回收 %d:%d ",*(int*)(pp +8),obj->flags); 79 | FL_UNSET(obj->flags, FL_ALLOC); 80 | Free(pp); 81 | } 82 | } 83 | 84 | } 85 | 86 | 87 | } 88 | } 89 | void tell_is_stackarg(void* arg){ 90 | void *top = get_sp(); 91 | if(sp_start > arg && arg > top){ 92 | if(gc_mark(*(void**)arg) == NOT_STACK){ 93 | mark(&Hugmem,*(void**)arg); 94 | } 95 | } 96 | } 97 | /** 98 | * 寄存器扫描 99 | */ 100 | void scan_register() 101 | { 102 | // printf("[gc] start scan register\n"); 103 | void *reg; 104 | if(reg = get_sp()) tell_is_stackarg(reg); 105 | if(reg = get_bp()) tell_is_stackarg(reg); 106 | if(reg = get_di()) tell_is_stackarg(reg); 107 | if(reg = get_si()) tell_is_stackarg(reg); 108 | if(reg = get_dx()) tell_is_stackarg(reg); 109 | if(reg = get_cx()) tell_is_stackarg(reg); 110 | if(reg = get_r8()) tell_is_stackarg(reg); 111 | if(reg = get_r9()) tell_is_stackarg(reg); 112 | if(reg = get_ax()) tell_is_stackarg(reg); 113 | if(reg = get_bx()) tell_is_stackarg(reg); 114 | } 115 | /** 116 | * 栈扫描 117 | */ 118 | void scan_stack(){ 119 | // printf("[gc] start scan stack\n"); 120 | //现在开始是真正的扫描系统栈空间 121 | void * cur_sp = get_sp(); 122 | //高低往低地址增长 123 | assert(sp_start >= cur_sp); 124 | for (; cur_sp < sp_start ; cur_sp += 4){ 125 | if(gc_mark(*(void**)cur_sp) == NOT_STACK){ 126 | mark(&Hugmem,*(void**)cur_sp); 127 | } 128 | } 129 | } 130 | /** 131 | * 标记清除算法的gc实现 132 | */ 133 | void gc(void) 134 | { 135 | // printf("[gc] start gc\n"); 136 | scan_register(); 137 | scan_stack(); 138 | 139 | //标记完成后 在进行 清除 对于没有标记过的进行回收 140 | gc_sweep(); 141 | } 142 | 143 | -------------------------------------------------------------------------------- /gc-try/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) $(wildcard *.s) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) ../gc.c -I ../ 13 | 14 | -------------------------------------------------------------------------------- /gc-try/README.md: -------------------------------------------------------------------------------- 1 | # 栈空间扫描 实现root的访问 2 | ```asciidoc 3 | void func(){ 4 | void *p = gc_malloc(...); 5 | } 6 | void main(){ 7 | func(); 8 | } 9 | ``` 10 | 11 | 1. 基于栈扫描的方式遍历可达对象 12 | 2. 不需要手动管理root,在gc的时候 会从该`寄存器rsp`执行的栈顶 遍历至`main`的栈地址 13 | 3. 从而实现可达对象的标记 -------------------------------------------------------------------------------- /gc-try/mark_sweep.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | #include "root.h" 3 | 4 | void *sp_start; 5 | 6 | 7 | /** 8 | * 对该对象进行标记 9 | * 并进行子对象标记 10 | * @param ptr 11 | */ 12 | void gc_mark(void * ptr) 13 | { 14 | GC_Heap *gh; 15 | Header *hdr; 16 | 17 | /* mark check */ 18 | if (!(gh = is_pointer_to_heap(ptr))){ 19 | // printf("not pointer\n"); 20 | return; 21 | } 22 | if (!(hdr = get_header(gh, ptr))) { 23 | printf("not find header\n"); 24 | return; 25 | } 26 | if (!FL_TEST(hdr, FL_ALLOC)) { 27 | printf("flag not set alloc\n"); 28 | return; 29 | } 30 | if (FL_TEST(hdr, FL_MARK)) { 31 | //printf("flag not set mark\n"); 32 | return; 33 | } 34 | 35 | /* marking */ 36 | FL_SET(hdr, FL_MARK); 37 | 38 | //进行child 节点递归 标记 39 | for (void* p = ptr; p < (void*)NEXT_HEADER(hdr); p++) { 40 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 41 | gc_mark(*(void **)p); 42 | } 43 | } 44 | /** 45 | * 清除 未标记内存 进行回收利用 46 | */ 47 | void gc_sweep(void) 48 | { 49 | size_t i; 50 | Header *p, *pend, *pnext; 51 | 52 | //遍历所有的堆内存 53 | //因为所有的内存都从堆里申请,所以需要遍历堆找出待回收的内存 54 | for (i = 0; i < gc_heaps_used; i++) { 55 | //pend 堆内存结束为止 56 | pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size); 57 | //堆的起始为止 因为堆的内存可能被分成了很多份,所以需要遍历该堆的内存 58 | for (p = gc_heaps[i].slot; p < pend; p = NEXT_HEADER(p)) { 59 | //查看该堆是否已经被使用 60 | if (FL_TEST(p, FL_ALLOC)) { 61 | //查看该堆是否被标记过 62 | if (FL_TEST(p, FL_MARK)) { 63 | DEBUG(printf("解除标记 : %p\n", p)); 64 | //取消标记,等待下次来回收,如果在下次回收前 65 | //1. 下次回收前发现该内存又被重新访问了,则不需要清除 66 | //2. 下次回收前发现该内存没有被访问过,所以需要清除 67 | FL_UNSET(p, FL_MARK); 68 | }else { 69 | DEBUG(printf("清除回收 :\n")); 70 | gc_free(p+1); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | /** 77 | * 寄存器扫描 78 | */ 79 | void scan_register() 80 | { 81 | void *reg; 82 | if(reg = get_sp()) gc_mark(*(void**)reg); 83 | if(reg = get_bp()) gc_mark(*(void**)reg); 84 | if(reg = get_di()) gc_mark(*(void**)reg); 85 | if(reg = get_si()) gc_mark(*(void**)reg); 86 | if(reg = get_dx()) gc_mark(*(void**)reg); 87 | if(reg = get_cx()) gc_mark(*(void**)reg); 88 | if(reg = get_r8()) gc_mark(*(void**)reg); 89 | if(reg = get_r9()) gc_mark(*(void**)reg); 90 | if(reg = get_ax()) gc_mark(*(void**)reg); 91 | if(reg = get_bx()) gc_mark(*(void**)reg); 92 | } 93 | /** 94 | * 栈扫描 95 | */ 96 | void scan_stack(){ 97 | //现在开始是真正的扫描系统栈空间 98 | void * cur_sp = get_sp(); 99 | //高低往低地址增长 100 | assert(sp_start >= cur_sp); 101 | for (; cur_sp < sp_start ; cur_sp += 4){ 102 | gc_mark(*(void**)cur_sp); 103 | } 104 | } 105 | /** 106 | * 标记清除算法的gc实现 107 | */ 108 | void gc(void) 109 | { 110 | scan_register(); 111 | scan_stack(); 112 | 113 | //标记完成后 在进行 清除 对于没有标记过的进行回收 114 | gc_sweep(); 115 | } 116 | 117 | -------------------------------------------------------------------------------- /gc-try/root.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName root 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2020/11/6 0006 下午 3:18 6 | *@Version 1.0 7 | **/ 8 | #ifndef GC_LEARNING_ROOT_H 9 | #define GC_LEARNING_ROOT_H 10 | 11 | void* get_sp()asm("get_sp"); 12 | void* get_bp()asm("get_bp"); 13 | void* get_di()asm("get_di"); 14 | void* get_si()asm("get_si"); 15 | void* get_dx()asm("get_dx"); 16 | void* get_cx()asm("get_cx"); 17 | void* get_r8()asm("get_r8"); 18 | void* get_r9()asm("get_r9"); 19 | void* get_bp()asm("get_bp"); 20 | void* get_ax()asm("get_ax"); 21 | void* get_bx()asm("get_bx"); 22 | 23 | #endif //GC_LEARNING_ROOT_H 24 | -------------------------------------------------------------------------------- /gc-try/root.s: -------------------------------------------------------------------------------- 1 | .text 2 | 3 | .globl get_sp 4 | get_sp: 5 | movq %rsp,%rax 6 | ret 7 | 8 | .globl get_bp 9 | get_bp: 10 | movq %rbp,%rax 11 | ret 12 | 13 | .globl get_di 14 | get_di: 15 | movq %rdi,%rax 16 | ret 17 | 18 | .globl get_si 19 | get_si: 20 | movq %rsi,%rax 21 | ret 22 | 23 | .globl get_dx 24 | get_dx: 25 | movq %rdx,%rax 26 | ret 27 | 28 | .globl get_cx 29 | get_cx: 30 | movq %rcx,%rax 31 | ret 32 | 33 | .globl get_r8 34 | get_r8: 35 | movq %r8,%rax 36 | ret 37 | 38 | .globl get_r9 39 | get_r9: 40 | movq %r9,%rax 41 | ret 42 | 43 | .globl get_ax 44 | get_ax: 45 | movq %rax,%rax 46 | ret 47 | 48 | .globl get_bx 49 | get_bx: 50 | movq %rbx,%rax 51 | ret 52 | -------------------------------------------------------------------------------- /gc-try/test.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | #include "root.h" 3 | 4 | extern void* sp_start; 5 | 6 | 7 | typedef struct obj{ 8 | int value; 9 | struct obj* next; 10 | }Obj; 11 | 12 | 13 | Obj *p1; 14 | Obj *p2; 15 | Obj *p3; 16 | 17 | 18 | void f3(){ 19 | Obj* tp3 = gc_malloc(sizeof(Obj)); 20 | p3 = tp3; 21 | p3->value = 3; 22 | } 23 | void f2(){ 24 | Obj* tp2 = gc_malloc(sizeof(Obj)); 25 | p2 = tp2; 26 | p2->value = 2; 27 | 28 | f3(); 29 | gc(); 30 | assert(p3->value == 0); 31 | assert(p2->value == 2); 32 | assert(p1->value == 1); 33 | } 34 | void f1(){ 35 | Obj* tp1 = gc_malloc(sizeof(Obj)); 36 | p1 = tp1; 37 | p1->value = 1; 38 | 39 | f2(); 40 | gc(); 41 | assert(p3->value == 0); 42 | assert(p2->value == 0); 43 | assert(p1->value == 1); 44 | } 45 | int main(int argc, char **argv) 46 | { 47 | //标记栈起始位置 48 | sp_start = get_sp(); 49 | f1(); 50 | gc(); 51 | assert(p3->value == 0); 52 | assert(p2->value == 0); 53 | assert(p1->value == 0); 54 | 55 | return 0; 56 | } -------------------------------------------------------------------------------- /gc.h: -------------------------------------------------------------------------------- 1 | #ifndef GC_H 2 | #define GC_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | /** 12 | * header头 每个用户用户申请的内存都有一个隐形的头部 13 | * 例如: gc_alloc(16) 实际申请了 16 + sizeof(header) 14 | * 那么返回给用户的地址其实是 ptr + sizeof(header) 15 | * 同样的也可以通过 ptr-sizeof(header) 拿到header头 16 | */ 17 | typedef struct header { 18 | size_t ref; //引用计数中使用,其他算法忽略 19 | size_t flags; //marked,remembered,copied 20 | size_t size; //当前内存块大小 21 | size_t age; //分代回收中使用,表示年龄 22 | struct header *next_free; //回收链表中使用,指向下一个空闲内存 23 | struct header *forwarding;//复制算法中使用, 指向拷贝后的新内存地址 24 | } Header; 25 | 26 | /** 27 | * 这个是一个内存池,用户层面的堆 28 | * 每个堆默认 1638 字节 29 | * 用户在申请内存时 从这1638上面分配一段地址 30 | * 会用在回收内存时 不用free,回收后会给其他的继续使用 (实现的机制就是上面的 header flags) 31 | */ 32 | typedef struct gc_heap { 33 | Header *slot; 34 | size_t size; 35 | } GC_Heap; 36 | /** 37 | * 根 38 | * 真正意义上的根可以为 全局变量、栈变量、寄存器变量等信息 39 | * 这里为了测试通过全局数组来模拟根 40 | */ 41 | typedef struct root_{ 42 | void *ptr; //从heap中申请的内存地址 43 | void *optr;//用户栈变量的地址 44 | }root; 45 | 46 | /* marco */ 47 | #define TINY_HEAP_SIZE 4 * 1024 //计算指针 所占内存大小 48 | #define PTRSIZE ((size_t) sizeof(void *)) 49 | #define HEADER_SIZE ((size_t) sizeof(Header))//堆的上限 50 | #define HEAP_LIMIT 100000 //字节对齐 向上取整 51 | #define ALIGN(x,a) (((x) + (a - 1)) & ~(a - 1)) 52 | #define NEXT_HEADER(x) ((Header *)((size_t)(x+1) + (x->size- HEADER_SIZE))) //[ [header] x->size [header] x->size ....] 53 | #define CURRENT_HEADER(x) ((Header *)x - 1) 54 | 55 | 56 | 57 | /* flags */ 58 | #define FL_ALLOC 0x1 59 | #define FL_MARK 0x2 60 | #define FL_COPIED 0x4 61 | #define FL_REMEMBERED 0x8 62 | #define FL_SET(x, f) (((Header *)x)->flags |= f) 63 | #define FL_UNSET(x, f) (((Header *)x)->flags &= ~(f)) 64 | #define FL_TEST(x, f) (((Header *)x)->flags & f) 65 | #define IS_MARKED(x) (FL_TEST(x, FL_ALLOC) && FL_TEST(x, FL_MARK)) 66 | #define IS_COPIED(x) (FL_TEST(x, FL_ALLOC) && FL_TEST(x, FL_COPIED)) 67 | #define IS_REMEMBERED(x) (FL_TEST(x, FL_ALLOC) && FL_TEST(x, FL_REMEMBERED)) 68 | 69 | /* public api */ 70 | void gc(void); //执行gc 垃圾回收 71 | void gc_init(size_t heap_size); //有些算法不需要动态扩充堆,那么就需要提前初始化堆 72 | void gc_free(void *ptr); //回收内存 73 | void* gc_malloc(size_t req_size); //内存分配 74 | GC_Heap* is_pointer_to_heap(void *ptr);//获取指针对应的堆首地址 75 | GC_Heap* is_pointer_to_space(void *ptr,size_t i); 76 | //安全的获取header头,可以通过内存段来定位该header 77 | // 因为如果ptr 刚好不在 header+1处的话 无法通过(ptr- sizeof(Header)) 来获取header地址 78 | Header* get_header(GC_Heap *gh, void *ptr); 79 | void add_heaps(size_t req_size); //扩充堆 80 | Header* gc_grow(size_t req_size); //某些算法在内存不够的时候会扩充堆 81 | 82 | 83 | /* global variable */ 84 | extern Header *free_list; 85 | extern GC_Heap gc_heaps[HEAP_LIMIT]; 86 | extern size_t gc_heaps_used; 87 | extern int auto_gc; //测试的时候 有时候需要关闭内存不够时执行gc 88 | extern int auto_grow; //测试的时候 有时候需要关闭内存不够时扩充堆 89 | 90 | 91 | /* roots */ 92 | #define ROOT_RANGES_LIMIT 100000 93 | #define DEBUG(exp) exp 94 | extern root roots[ROOT_RANGES_LIMIT]; 95 | extern size_t root_used; 96 | void add_roots(void* o_ptr); //模拟根,真正意义的根来自全局、栈、寄存器等、这里模拟用数组实现 97 | #endif -------------------------------------------------------------------------------- /generational/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) ../gc.c -I ../ 13 | 14 | -------------------------------------------------------------------------------- /generational/README.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /generational/generational.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName copying 3 | *@Author brewlin 4 | *@Date 2020/10/22 0022 上午 11:01 5 | *@Version 1.0 6 | **/ 7 | #ifndef GC_LEARNING_COPYING_H 8 | #define GC_LEARNING_COPYING_H 9 | 10 | #include "gc.h" 11 | 12 | #define AGE_MAX 3 13 | 14 | /** 15 | * 写入屏障 16 | * 在更新对象引用的时候 需要判断obj是否是老年代对象 new_obj为年轻代对象 17 | * 如果是 则需要加入 记忆结果集 18 | * @param obj 19 | * @param field 20 | * @param new_obj 21 | */ 22 | void write_barrier(void *obj_ptr,void *field,void* new_obj_ptr); 23 | void* major_malloc(size_t req_size); 24 | void* minor_malloc(size_t req_size); 25 | void major_gc(void); 26 | void minor_gc(void); 27 | 28 | //生成空间 new generational 29 | extern int newg; 30 | //幸存空间1 survivor1 generational 31 | extern int survivorfromg; 32 | //幸存空间2 survivor1 generational 33 | extern int survivortog; 34 | //老年代空间 old generational 35 | extern int oldg; 36 | //保存对象引用关系同时存在新生代和老年代的 对象 37 | extern void* rs[ROOT_RANGES_LIMIT]; 38 | extern int rs_index; 39 | 40 | extern void* to_free_p; 41 | extern Header* new_free_p; 42 | 43 | #endif //GC_LEARNING_COPYING_H 44 | -------------------------------------------------------------------------------- /generational/mark_sweep.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 专用于老年代gc的标记清除算法 3 | */ 4 | 5 | #include "gc.h" 6 | #include "generational.h" 7 | /** 8 | * 对该对象进行标记 9 | * 并进行子对象标记 10 | * @param ptr 11 | */ 12 | void gc_mark(void * ptr) 13 | { 14 | GC_Heap *gh; 15 | Header *hdr; 16 | //老年代gc 只需要检查是否是老年代区即可 17 | if (!(gh = is_pointer_to_space(ptr,oldg))){ 18 | // printf("not pointer\n"); 19 | return; 20 | } 21 | if (!(hdr = get_header(gh,ptr))) { 22 | printf("not find header\n"); 23 | return; 24 | } 25 | if (!FL_TEST(hdr, FL_ALLOC)) { 26 | printf("flag not set alloc\n"); 27 | return; 28 | } 29 | if (FL_TEST(hdr, FL_MARK)) { 30 | //printf("flag not set mark\n"); 31 | return; 32 | } 33 | 34 | /* marking */ 35 | FL_SET(hdr, FL_MARK); 36 | // printf("mark ptr : %p, header : %p\n", ptr, hdr); 37 | //进行子节点递归 标记 38 | for (void* p = ptr + 1; p < (void*)NEXT_HEADER(hdr); p++) { 39 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 40 | gc_mark(*(void **)p); 41 | } 42 | 43 | } 44 | /** 45 | * 清除 未标记内存 进行回收利用 46 | */ 47 | void gc_sweep(void) 48 | { 49 | size_t i; 50 | Header *p, *pend, *pnext; 51 | 52 | //遍历所有的堆内存 53 | //因为所有的内存都从堆里申请,所以需要遍历堆找出待回收的内存 54 | 55 | //老年代gc 只清除 老年代堆即可 56 | for (p = gc_heaps[oldg].slot; (void*)p < (void*)((size_t)gc_heaps[oldg].slot + gc_heaps[oldg].size) ; p = NEXT_HEADER(p)) { 57 | //查看该堆是否已经被使用 58 | if (FL_TEST(p, FL_ALLOC)) { 59 | //查看该堆是否被标记过 60 | if (FL_TEST(p, FL_MARK)) { 61 | DEBUG(printf("解除标记 : %p\n", p)); 62 | //取消标记,等待下次来回收,如果在下次回收前 63 | //1. 下次回收前发现该内存又被重新访问了,则不需要清除 64 | //2. 下次回收前发现该内存没有被访问过,所以需要清除 65 | FL_UNSET(p, FL_MARK); 66 | }else { 67 | DEBUG(printf("清除回收 :\n")); 68 | gc_free(p+1); 69 | } 70 | } 71 | } 72 | } 73 | 74 | /** 75 | * 老年代分配 76 | * 老年代分配走 标记清除算法,所以需要用到空闲链表 77 | */ 78 | void* major_malloc(size_t req_size) 79 | { 80 | req_size -= HEADER_SIZE; 81 | //默认的gc_malloc 是老年代分配 82 | return gc_malloc(req_size); 83 | } 84 | 85 | void major_gc(void) 86 | { 87 | //默认的gc是执行标记清除算法 执行的是老年代gc 88 | gc(); 89 | } 90 | /** 91 | * 老年代 gc 92 | */ 93 | void gc(void) 94 | { 95 | //rs 里的基本都是老年代 96 | for(int i = 0; i < rs_index; i ++) { 97 | //只对老年代 对象进行gc 98 | gc_mark(rs[i]); 99 | } 100 | //标记完成后 在进行 清除 对于没有标记过的进行回收 101 | gc_sweep(); 102 | } 103 | 104 | -------------------------------------------------------------------------------- /heap.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /mark-sweep-bitmap/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) -I ../ 13 | 14 | -------------------------------------------------------------------------------- /mark-sweep-bitmap/README.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) 2 | 3 | 1. 位图法不需要在对象头添加附加信息,所以不能公用gc.h实现 4 | 2. 规定 1个bit位 标记 对应8字节64位的对象信息 5 | -------------------------------------------------------------------------------- /mark-sweep-bitmap/gc.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | #include 3 | 4 | //回收链表,挂着空闲链表 5 | Header* free_list = NULL; 6 | GC_Heap heap; 7 | root roots[ROOT_RANGES_LIMIT]; //根 8 | size_t root_used = 0; 9 | 10 | /** 11 | * 为了更好的描述位图标记,目前只初始化话一个堆 12 | * 且不允许扩充堆 13 | */ 14 | /** 15 | * 增加堆 16 | **/ 17 | void gc_init() 18 | { 19 | void *p; 20 | if((p = sbrk(TINY_HEAP_SIZE)) == (void *)-1){ 21 | DEBUG(printf("sbrk 分配内存失败\n")); 22 | return NULL; 23 | } 24 | heap.slot = p; 25 | heap.size = TINY_HEAP_SIZE; 26 | heap.end = p + TINY_HEAP_SIZE; 27 | memset(p,0,heap.size); 28 | //这里要将位图和内存区区分开来 29 | // 1bit 对应8字节对象,是64倍关系 30 | int bit = ALIGN(TINY_HEAP_SIZE/(PTRSIZE*8 + 1),64); 31 | heap.bit_size = bit; 32 | int obj_size = TINY_HEAP_SIZE - bit; 33 | heap.bit = heap.slot + bit; 34 | 35 | //heap.bit 之后的位置才是内存分配区 36 | Header* up = (Header *) heap.bit; 37 | up->size = obj_size; 38 | //初始化空闲链表 39 | free_list = up; 40 | } 41 | /** 42 | * 将分配的变量 添加到root 引用,只要是root上的对象都能够进行标记 43 | * @param start 44 | * @param end 45 | */ 46 | void add_roots(void* ptr) 47 | { 48 | roots[root_used].ptr = ptr; 49 | root_used++; 50 | if (root_used >= ROOT_RANGES_LIMIT) { 51 | fputs("Root OverFlow", stderr); 52 | abort(); 53 | } 54 | } 55 | 56 | 57 | /** 58 | * 分配一块内存 59 | */ 60 | void* gc_malloc(size_t req_size) 61 | { 62 | DEBUG(printf("内存申请 :%ld\n",req_size)); 63 | Header *p, *prevp; 64 | size_t do_gc = 0; 65 | 66 | req_size += HEADER_SIZE; 67 | //对齐 字节 68 | req_size = ALIGN(req_size, PTRSIZE); 69 | 70 | if (req_size <= 0) { 71 | return NULL; 72 | } 73 | alloc: 74 | //从空闲链表上去搜寻 空余空间 75 | prevp = free_list; 76 | //死循环 遍历 77 | for (p = prevp; p; prevp = p, p = p->next_free) { 78 | //堆的内存足够 79 | if (p->size >= req_size) { 80 | //刚好满足 81 | if (p->size == req_size) 82 | /* 刚好满足 */ 83 | // 从空闲列表上 移除当前的 堆,因为申请的大小刚好把堆消耗完了 84 | if(p == prevp) 85 | free_list = prevp = p->next_free; 86 | else 87 | prevp->next_free = p->next_free; 88 | 89 | //没有刚好相同的空间,所以从大分块中拆分一块出来给用户 90 | //这里因为有拆分 所以会导致内存碎片的问题,这也是 标记清除算法的一个缺点 91 | //就是导致内存碎片 92 | else { 93 | /* too big */ 94 | // p->size -= (req_size + HEADER_SIZE); 95 | // 这里就是从当前堆的堆首 跳转到末尾申请的那个堆 96 | // p = NEXT_HEADER(p); 97 | prevp = (void*)prevp + req_size; 98 | memcpy(prevp,p,HEADER_SIZE); 99 | prevp->size = p->size - req_size; 100 | } 101 | p->size = req_size; 102 | free_list = prevp; 103 | 104 | //新的内存 是包括了 header + mem 所以返回给 用户mem部分就可以了 105 | return (void *)(p+1); 106 | } 107 | 108 | } 109 | 110 | //这里表示前面多次都没有找到合适的空间,且已经遍历完了空闲链表 free_list 111 | //这里表示在 单次内存申请的时候 且 空间不够用的情况下 需要执行一次gc 112 | //一般是分块用尽会 才会执行gc 清除带回收的内存 113 | if (!do_gc) { 114 | gc(); 115 | do_gc = 1; 116 | goto alloc; 117 | } 118 | return NULL; 119 | 120 | } 121 | /** 122 | * 传入的是一个内存地址 是不带header头的 123 | * 所以我们可以通过(Header*)ptr - 1 来定位到header头 124 | **/ 125 | void gc_free(void *ptr) 126 | { 127 | DEBUG(printf("释放内存 :%p free_list:%p\n",ptr,free_list)); 128 | Header *target, *hit,*prevp; 129 | //通过内存地址向上偏移量找到 header头 130 | target = (Header *)ptr - 1; 131 | //回收的数据立马清空 132 | // memset(ptr,0,target->size-HEADER_SIZE); 133 | 134 | //空闲链表为空,直接将当前target挂到上面 135 | if(free_list == NULL){ 136 | free_list = target; 137 | return; 138 | } 139 | //特殊情况,如果target->next == free_list 在上面是无法判断的 140 | if(NEXT_HEADER(target) == free_list){ 141 | target->size += (free_list->size); 142 | target->next_free = free_list->next_free; 143 | free_list = target; 144 | return; 145 | } 146 | //搜索target可能在空闲链表上的区间位置 147 | prevp = free_list; 148 | for(hit = prevp; hit && hit->next_free ; prevp = hit,hit = hit->next_free) 149 | { 150 | //刚好 target就在 [hit,hit->next_free] 之间 151 | if(target >= hit && target <= hit->next_free){ 152 | break; 153 | } 154 | //跨堆的情况 说明target在两个堆之间 (heap1_end,heap2_start) 155 | if(hit >= hit->next_free && (target > hit || target < hit->next_free)) 156 | break; 157 | } 158 | 159 | //1. 判断右区间 如果target属于右区间 则合并 160 | if (NEXT_HEADER(target) == hit->next_free) { 161 | target->size += hit->next_free->size; 162 | target->next_free = hit->next_free->next_free; 163 | }else { 164 | target->next_free = hit->next_free; 165 | } 166 | 167 | //2. 判断左区间 如果target属于左区间 则合并 168 | if (NEXT_HEADER(hit) == target) { 169 | /* merge */ 170 | hit->size += target->size; 171 | hit->next_free = target->next_free; 172 | }else { 173 | hit->next_free = target; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /mark-sweep-bitmap/gc.h: -------------------------------------------------------------------------------- 1 | #ifndef GC_H 2 | #define GC_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | /** 12 | * header头 每个用户用户申请的内存都有一个隐形的头部 13 | * 例如: gc_alloc(16) 实际申请了 16 + sizeof(header) 14 | * 那么返回给用户的地址其实是 ptr + sizeof(header) 15 | * 同样的也可以通过 ptr-sizeof(header) 拿到header头 16 | */ 17 | typedef struct header { 18 | size_t size; //当前内存块大小 19 | struct header *next_free; //回收链表中使用,指向下一个空闲内存 20 | } Header; 21 | 22 | /** 23 | * 这个是一个内存池,用户层面的堆 24 | * 每个堆默认 1638 字节 25 | * 用户在申请内存时 从这1638上面分配一段地址 26 | * 会用在回收内存时 不用free,回收后会给其他的继续使用 (实现的机制就是上面的 header flags) 27 | */ 28 | typedef struct gc_heap { 29 | //内存开始地址 30 | Header *slot; 31 | //bit位的起始位置 32 | Header *bit; 33 | //位图大小 34 | int bit_size; 35 | Header *end; 36 | size_t size; 37 | } GC_Heap; 38 | /** 39 | * 根 40 | * 真正意义上的根可以为 全局变量、栈变量、寄存器变量等信息 41 | * 这里为了测试通过全局数组来模拟根 42 | */ 43 | typedef struct root_{ 44 | void *ptr; //从heap中申请的内存地址 45 | void *optr;//用户栈变量的地址 46 | }root; 47 | 48 | /* marco */ 49 | #define TINY_HEAP_SIZE 4 * 1024 //计算指针 所占内存大小 50 | #define PTRSIZE ((size_t) sizeof(void *)) 51 | #define HEADER_SIZE ((size_t) sizeof(Header))//堆的上限 52 | #define ALIGN(x,a) (((x) + (a - 1)) & ~(a - 1)) 53 | #define NEXT_HEADER(x) ((Header *)((size_t)(x+1) + (x->size- HEADER_SIZE))) //[ [header] x->size [header] x->size ....] 54 | #define CURRENT_HEADER(x) ((Header *)x - 1) 55 | 56 | 57 | 58 | 59 | /* public api */ 60 | void gc(void); //执行gc 垃圾回收 61 | void gc_free(void *ptr); //回收内存 62 | void* gc_malloc(size_t req_size); //内存分配 63 | #define IS_HEAP(ptr) ((((void *)heap.bit) <= ptr) && ((size_t)ptr < (size_t)heap.end)) 64 | void gc_init(); 65 | /* global variable */ 66 | extern Header *free_list; 67 | extern GC_Heap heap; 68 | 69 | 70 | /* roots */ 71 | #define ROOT_RANGES_LIMIT 100000 72 | #define DEBUG(exp) exp 73 | extern root roots[ROOT_RANGES_LIMIT]; 74 | extern size_t root_used; 75 | void add_roots(void* o_ptr); //模拟根,真正意义的根来自全局、栈、寄存器等、这里模拟用数组实现 76 | #endif -------------------------------------------------------------------------------- /mark-sweep-bitmap/mark_sweep.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | 3 | 4 | /** 5 | * 对该对象进行标记 6 | * 并进行子对象标记 7 | * @param ptr 8 | */ 9 | void gc_mark(void * ptr) 10 | { 11 | GC_Heap *gh; 12 | Header *hdr; 13 | 14 | /* mark check */ 15 | if (!IS_HEAP(ptr)){ 16 | // printf("not pointer\n"); 17 | return; 18 | } 19 | //进行标记 20 | int obj = (long int)((void*)(CURRENT_HEADER(ptr)) - (void*)heap.bit) / PTRSIZE; 21 | int index = obj / PTRSIZE; 22 | int offset = obj % PTRSIZE; 23 | //进行位标记 24 | void *bit = (void*)heap.slot + index; 25 | *(char*)bit |= 1 << offset; 26 | 27 | hdr = CURRENT_HEADER(ptr); 28 | //进行child 节点递归 标记 29 | for (void* p = ptr; p < (void*)NEXT_HEADER(hdr); p++) { 30 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 31 | gc_mark(*(void **)p); 32 | } 33 | } 34 | /** 35 | * 清除 未标记内存 进行回收利用 36 | */ 37 | void gc_sweep(void) 38 | { 39 | size_t i; 40 | Header *prev,*p, *pend, *pnext; 41 | int obj,index,offset; 42 | 43 | 44 | for (p = heap.bit; p < heap.end && prev != p; prev = p,p = NEXT_HEADER(p)) { 45 | //查看位图是否已经标记 46 | obj = (long int )((void*)p - (void*)heap.bit) / PTRSIZE; 47 | index = obj / PTRSIZE; 48 | offset = obj % PTRSIZE; 49 | 50 | void *bit = (void*)heap.slot + index; 51 | //查看该堆是否已经被使用 ,没有被标记的白色垃圾需要回收 52 | if ((*(unsigned char*)bit & (1 << offset)) == 0) { 53 | // DEBUG(printf("清除回收 :\n")); 54 | gc_free(p+1); 55 | } 56 | } 57 | //最后清空位图 58 | memset(heap.slot,0,heap.bit_size); 59 | } 60 | /** 61 | * 标记清除算法的gc实现 62 | */ 63 | void gc(void) 64 | { 65 | printf("gc start\n"); 66 | //垃圾回收前 先从 root 开始 进行递归标记 67 | for(int i = 0;i < root_used;i++) 68 | gc_mark(roots[i].ptr); 69 | //标记完成后 在进行 清除 对于没有标记过的进行回收 70 | gc_sweep(); 71 | } 72 | 73 | -------------------------------------------------------------------------------- /mark-sweep-bitmap/test.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | 3 | int clear(){ 4 | for (int j = 0; j <= root_used ; ++j){ 5 | roots[j].ptr = NULL; 6 | roots[j].optr = NULL; 7 | } 8 | root_used = 0; 9 | } 10 | void test_malloc_speed(){ 11 | clock_t start,end; 12 | double duration; 13 | start = clock(); 14 | 15 | for (int i = 0; i < 100; ++i) { 16 | int size = rand()%90; 17 | void *p = gc_malloc(size); 18 | if(!p){ 19 | // printf("even gc(),still out of memory!\n"); 20 | //清除根 达到回收全部对象的效果 21 | clear(); 22 | }else{ 23 | add_roots(p); 24 | } 25 | // gc_free(p); 26 | } 27 | for (int i = 0; i < 100; ++i) { 28 | int size = rand() % 90; 29 | void *p = gc_malloc(size); 30 | if(!p) { 31 | printf("even gc(),still out of memory!\n"); 32 | } 33 | } 34 | end = clock(); 35 | duration = (double)(end - start)/CLOCKS_PER_SEC; 36 | printf("execution seconds:%f\n",duration); 37 | } 38 | int main(int argc, char **argv) 39 | { 40 | gc_init(); 41 | 42 | test_malloc_speed(); 43 | return 0; 44 | } -------------------------------------------------------------------------------- /mark-sweep-multi-free-list/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) 13 | 14 | -------------------------------------------------------------------------------- /mark-sweep-multi-free-list/README.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /mark-sweep-multi-free-list/gc.h: -------------------------------------------------------------------------------- 1 | #ifndef GC_H 2 | #define GC_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | 14 | /** 15 | * header头 每个用户用户申请的内存都有一个隐形的头部 16 | * 例如: gc_alloc(16) 实际申请了 16 + sizeof(header) 17 | * 那么返回给用户的地址其实是 ptr + sizeof(header) 18 | * 同样的也可以通过 ptr-sizeof(header) 拿到header头 19 | */ 20 | typedef struct header { 21 | size_t flags; 22 | size_t size; 23 | struct header *next_free; 24 | } Header; 25 | 26 | /** 27 | * 这个是一个内存池,用户层面的堆 28 | * 每个堆默认 1638 字节 29 | * 用户在申请内存时 从这1638上面分配一段地址 30 | * 会用在回收内存时 不用free,回收后会给其他的继续使用 (实现的机制就是上面的 header flags) 31 | */ 32 | typedef struct gc_heap { 33 | Header *slot; 34 | size_t size; 35 | } GC_Heap; 36 | 37 | #define TINY_HEAP_SIZE 0x4000 38 | //计算指针 所占内存大小 39 | #define PTRSIZE ((size_t) sizeof(void *)) 40 | #define ALIGNMENT 8 41 | #define ALIGNMENT_SHIFT 3 42 | 43 | #define HEADER_SIZE ((size_t) sizeof(Header)) 44 | //堆的上限 45 | #define HEAP_LIMIT 1000000 46 | //字节对齐 向上取整 47 | #define ALIGN(x,a) (((x) + (a - 1)) & ~(a - 1)) 48 | //x为一个header*,那么通过当前的对象 可以找到下一个使用的对象地址 49 | //[ [header] x->size [header] x->size ....] 50 | #define NEXT_HEADER(x) ((Header *)((size_t)(x+1) + x->size)) 51 | #define CURRENT_HEADER(x) ((Header *)x - 1) 52 | 53 | 54 | /* flags */ 55 | #define FL_ALLOC 0x1 56 | #define FL_MARK 0x2 57 | #define FL_SET(x, f) (((Header *)x)->flags |= f) 58 | #define FL_UNSET(x, f) (((Header *)x)->flags &= ~(f)) 59 | #define FL_TEST(x, f) (((Header *)x)->flags & f) 60 | #define IS_MARKED(x) (FL_TEST(x, FL_ALLOC) && FL_TEST(x, FL_MARK)) 61 | 62 | #define ROOT_RANGES_LIMIT 100000 63 | 64 | #define DEBUG(exp) 65 | 66 | 67 | //回收内存 68 | void gc_free(void *ptr); 69 | //从堆缓存中申请一份内存 70 | void * gc_malloc(size_t req_size); 71 | //执行gc 垃圾回收 72 | void gc(void); 73 | 74 | //定位内存实际所在的堆 75 | //如果没有找到,说明该内存非 堆内存池中申请的内存 76 | GC_Heap* is_pointer_to_heap(void *ptr); 77 | //安全的获取header头,可以通过内存段来定位该header 78 | // 因为如果ptr 刚好不在 header+1处的话 无法通过(ptr- sizeof(Header)) 来获取header地址 79 | Header* get_header(GC_Heap *gh, void *ptr); 80 | 81 | void gc(void); 82 | //回收链表,挂着空闲链表 83 | extern Header *free_list[33]; 84 | extern GC_Heap gc_heaps[HEAP_LIMIT]; 85 | extern size_t gc_heaps_used; 86 | extern int auto_gc; 87 | 88 | #define MAX_SLICE_HEAP 31 89 | #define HUGE_BLOCK 32 90 | 91 | /****** 标记清除法实现-------------*/ 92 | void gc_mark(void * ptr); 93 | void gc_mark_range(void *start, void *end); 94 | void gc_sweep(void); 95 | void add_roots(void * obj); 96 | typedef struct root_range { 97 | void *start; 98 | void *end; 99 | }root; 100 | extern root roots[ROOT_RANGES_LIMIT]; 101 | extern size_t root_used; 102 | #endif -------------------------------------------------------------------------------- /mark-sweep-multi-free-list/mark_sweep.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | 3 | //保存了所有申请的对象 4 | root roots[ROOT_RANGES_LIMIT]; 5 | size_t root_used = 0; 6 | 7 | /** 8 | * 对该对象进行标记 9 | * 并进行子对象标记 10 | * @param ptr 11 | */ 12 | void gc_mark(void * ptr) 13 | { 14 | GC_Heap *gh; 15 | Header *hdr; 16 | 17 | /* mark check */ 18 | if (!(gh = is_pointer_to_heap(ptr))){ 19 | // printf("not pointer\n"); 20 | return; 21 | } 22 | if (!(hdr = get_header(gh, ptr))) { 23 | DEBUG(printf("not find header\n")); 24 | return; 25 | } 26 | if (!FL_TEST(hdr, FL_ALLOC)) { 27 | DEBUG(printf("flag not set alloc\n")); 28 | return; 29 | } 30 | if (FL_TEST(hdr, FL_MARK)) { 31 | //printf("flag not set mark\n"); 32 | return; 33 | } 34 | 35 | /* marking */ 36 | FL_SET(hdr, FL_MARK); 37 | DEBUG(printf("mark ptr : %p, header : %p\n", ptr, hdr)); 38 | //进行子节点递归 标记 39 | gc_mark_range((void *)(hdr+1), (void *)NEXT_HEADER(hdr)); 40 | } 41 | /** 42 | * 遍历root 进行标记 43 | * @param start 44 | * @param end 45 | */ 46 | void gc_mark_range(void *start, void *end) 47 | { 48 | void *p; 49 | 50 | //start 表示当前对象 需要释放 51 | gc_mark(start); 52 | //可能申请的内存 里面又包含了其他内存 53 | for (p = start+1; p < end; p++) { 54 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 55 | gc_mark(*(void **)p); 56 | } 57 | } 58 | /** 59 | * 清除 未标记内存 进行回收利用 60 | */ 61 | void gc_sweep(void) 62 | { 63 | size_t i; 64 | Header *p, *pend, *pnext; 65 | 66 | //遍历所有的堆内存 67 | //因为所有的内存都从堆里申请,所以需要遍历堆找出待回收的内存 68 | for (i = 0; i < gc_heaps_used; i++) { 69 | //pend 堆内存结束为止 70 | pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size); 71 | //堆的起始为止 因为堆的内存可能被分成了很多份,所以需要遍历该堆的内存 72 | for (p = gc_heaps[i].slot; p < pend; p = NEXT_HEADER(p)) { 73 | //查看该堆是否已经被使用 74 | if (FL_TEST(p, FL_ALLOC)) { 75 | //查看该堆是否被标记过 76 | if (FL_TEST(p, FL_MARK)) { 77 | DEBUG(printf("mark unset : %p\n", p)); 78 | //取消标记,等待下次来回收,如果在下次回收前 79 | //1. 下次回收前发现该内存又被重新访问了,则不需要清除 80 | //2. 下次回收前发现该内存没有被访问过,所以需要清除 81 | FL_UNSET(p, FL_MARK); 82 | }else { 83 | DEBUG(printf("gc sweep:find one need clear\n")); 84 | gc_free(p+1); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | /** 91 | * 将分配的变量 添加到root 引用,只要是root上的对象都能够进行标记 92 | * @param start 93 | * @param end 94 | */ 95 | void add_roots(void * obj) 96 | { 97 | roots[root_used].start = obj; 98 | roots[root_used].end = obj + CURRENT_HEADER(obj)->size; 99 | root_used++; 100 | 101 | if (root_used >= ROOT_RANGES_LIMIT) { 102 | fputs("Root OverFlow", stderr); 103 | abort(); 104 | } 105 | } 106 | void gc(void) 107 | { 108 | //垃圾回收前 先从 root 开始 进行递归标记 109 | for(int i = 0;i < root_used;i++){ 110 | gc_mark_range(roots[i].start, roots[i].end); 111 | } 112 | //标记完成后 在进行 清除 对于没有标记过的进行回收 113 | gc_sweep(); 114 | } 115 | 116 | -------------------------------------------------------------------------------- /mark-sweep-multi-free-list/test.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | int clear(){ 3 | for (int i = 0; i < 33 ; ++i) 4 | free_list[i] = NULL; 5 | for (int i = 0; i <= gc_heaps_used; ++i){ 6 | gc_heaps[i].size = 0; 7 | gc_heaps[i].slot = NULL; 8 | } 9 | gc_heaps_used = 0; 10 | 11 | for (int j = 0; j <= root_used ; ++j){ 12 | roots[j].start = NULL; 13 | roots[j].end = NULL; 14 | } 15 | root_used = 0; 16 | } 17 | /** 18 | * 1. 测试条件保证申请内存保证在TINY_HEAP_SIZE下,因为大于这个数会发生gc 19 | * 测试 分配和 释放是否正常 20 | */ 21 | void test_malloc_free(size_t req_size){ 22 | printf("-----------测试内存申请与释放------------\n"); 23 | printf("-----------***************------------\n"); 24 | //在回收p1的情况下 p2的申请将复用p1的地址 25 | void *p1 = gc_malloc(req_size); 26 | gc_free(p1); 27 | void *p2 = gc_malloc(req_size); 28 | //在上面 p1被释放了,p2 从新申请 会继续从堆的起始位置开始分配 所以 内存地址是一样的 29 | assert(p1 == p2); 30 | gc_free(p2); 31 | 32 | //因为没有空闲的,所以即使不清理的情况下还是会触发gc 33 | p1 = gc_malloc(req_size); 34 | p2 = gc_malloc(req_size); 35 | assert(p1 == p2); 36 | 37 | printf("----------- passing ------------\n\n"); 38 | clear(); 39 | } 40 | void test_gc(size_t req_size){ 41 | printf("-----------测试gc ------------\n"); 42 | printf("-----------***************------------\n"); 43 | void *p1 = gc_malloc(req_size); 44 | gc(); 45 | void *p2 = gc_malloc(req_size); 46 | //执行gc后 p1被回收 47 | assert(p1 == p2); 48 | clear(); 49 | 50 | //因为该变量被root引用 所以不会被gc 51 | p1 = gc_malloc(req_size); 52 | add_roots(p1); 53 | gc(); 54 | p2 = gc_malloc(req_size); 55 | assert(p1 != p2); 56 | 57 | printf("----------- passing ------------\n\n"); 58 | clear(); 59 | } 60 | void test_large_gc(){ 61 | printf("-----------测试大内存gc ------------\n"); 62 | printf("-----------***************------------\n"); 63 | void *p1 = gc_malloc(TINY_HEAP_SIZE); 64 | void *p2 = gc_malloc(TINY_HEAP_SIZE); 65 | //因为 req_size 已经达到堆内存了上限了,且无可用内存 会自动执行gc 66 | assert(p1 == p2); 67 | clear(); 68 | 69 | p1 = gc_malloc(TINY_HEAP_SIZE); 70 | //将对象加入root 71 | add_roots(p1); 72 | p2 = gc_malloc(TINY_HEAP_SIZE); 73 | //p1 不会被回收 74 | //p2 会申请新的内存 75 | assert(p1 != p2); 76 | 77 | printf("----------- passing ------------\n"); 78 | clear(); 79 | } 80 | 81 | /** 82 | * 对引用进行测试 83 | */ 84 | void test_reference_gc() 85 | { 86 | printf("-----------测试引用gc ------------\n"); 87 | printf("-----------***************------------\n"); 88 | typedef struct obj{ 89 | int v; 90 | struct obj* left; 91 | struct obj* right; 92 | }Obj; 93 | 94 | Obj* p = gc_malloc(sizeof(Obj)); 95 | p->v = 10; 96 | //p 已经被释放了 97 | p->left = gc_malloc(sizeof(Obj)); 98 | gc(); 99 | assert(p->v == 0); 100 | assert(p->left == NULL); 101 | 102 | 103 | p = gc_malloc(sizeof(Obj)); 104 | //加入root 即使left right 没有加入 但是他们作为 p的子节点引用 会被标记 105 | add_roots(p); 106 | p->v = 10; 107 | p->left = gc_malloc(sizeof(Obj)); 108 | p->left->v = 11; 109 | p->right = gc_malloc(sizeof(Obj)); 110 | p->right->v = 12; 111 | gc(); 112 | assert(p->v == 10); 113 | assert(p->left->v == 11); 114 | assert(p->right->v == 12); 115 | 116 | 117 | printf("----------- passing ------------\n"); 118 | clear(); 119 | } 120 | /** 121 | * 测试的时候需要关闭 自动gc 122 | */ 123 | void test_malloc_speed(){ 124 | auto_gc = 0; 125 | time_t start,end; 126 | start = time(NULL); 127 | for (int i = 0; i < 100; ++i) { 128 | int size = rand()%90; 129 | void *p = gc_malloc(size); 130 | } 131 | gc(); 132 | for (int i = 0; i < 100; ++i) { 133 | int size = rand() % 90; 134 | void *p = gc_malloc(size); 135 | } 136 | end = time(NULL); 137 | printf("execution seconds:%f\n",difftime(end,start)); 138 | } 139 | int main(int argc, char **argv) 140 | { 141 | //小内存测试, 142 | test_malloc_free(8); 143 | clear(); 144 | //大内存测试 145 | test_malloc_free(TINY_HEAP_SIZE); 146 | clear(); 147 | //测试gc 148 | test_gc(8); 149 | clear(); 150 | //大内存 无需手动gc 测试 151 | test_large_gc(); 152 | clear(); 153 | //对象引用测试 154 | test_reference_gc(); 155 | clear(); 156 | //测试分配速度 157 | // test_malloc_speed(); 158 | return 0; 159 | } -------------------------------------------------------------------------------- /mark-sweep/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) ../gc.c -I ../ 13 | 14 | -------------------------------------------------------------------------------- /mark-sweep/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | //当前算法申请内存需要使用minor_malloc 3 | 4 | void *p = minor_malloc(size); 5 | ``` 6 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /mark-sweep/mark_sweep.c: -------------------------------------------------------------------------------- 1 | #include "../gc.h" 2 | 3 | 4 | /** 5 | * 对该对象进行标记 6 | * 并进行子对象标记 7 | * @param ptr 8 | */ 9 | void gc_mark(void * ptr) 10 | { 11 | GC_Heap *gh; 12 | Header *hdr; 13 | 14 | /* mark check */ 15 | if (!(gh = is_pointer_to_heap(ptr))){ 16 | // printf("not pointer\n"); 17 | return; 18 | } 19 | if (!(hdr = get_header(gh, ptr))) { 20 | printf("not find header\n"); 21 | return; 22 | } 23 | if (!FL_TEST(hdr, FL_ALLOC)) { 24 | printf("flag not set alloc\n"); 25 | return; 26 | } 27 | if (FL_TEST(hdr, FL_MARK)) { 28 | //printf("flag not set mark\n"); 29 | return; 30 | } 31 | 32 | /* marking */ 33 | FL_SET(hdr, FL_MARK); 34 | 35 | //进行child 节点递归 标记 36 | for (void* p = ptr; p < (void*)NEXT_HEADER(hdr); p++) { 37 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 38 | gc_mark(*(void **)p); 39 | } 40 | } 41 | /** 42 | * 清除 未标记内存 进行回收利用 43 | */ 44 | void gc_sweep(void) 45 | { 46 | size_t i; 47 | Header *p, *pend, *pnext; 48 | 49 | //遍历所有的堆内存 50 | //因为所有的内存都从堆里申请,所以需要遍历堆找出待回收的内存 51 | for (i = 0; i < gc_heaps_used; i++) { 52 | //pend 堆内存结束为止 53 | pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size); 54 | //堆的起始为止 因为堆的内存可能被分成了很多份,所以需要遍历该堆的内存 55 | for (p = gc_heaps[i].slot; p < pend; p = NEXT_HEADER(p)) { 56 | //查看该堆是否已经被使用 57 | if (FL_TEST(p, FL_ALLOC)) { 58 | //查看该堆是否被标记过 59 | if (FL_TEST(p, FL_MARK)) { 60 | DEBUG(printf("解除标记 : %p\n", p)); 61 | //取消标记,等待下次来回收,如果在下次回收前 62 | //1. 下次回收前发现该内存又被重新访问了,则不需要清除 63 | //2. 下次回收前发现该内存没有被访问过,所以需要清除 64 | FL_UNSET(p, FL_MARK); 65 | }else { 66 | DEBUG(printf("清除回收 :\n")); 67 | gc_free(p+1); 68 | } 69 | } 70 | } 71 | } 72 | } 73 | /** 74 | * 标记清除算法的gc实现 75 | */ 76 | void gc(void) 77 | { 78 | //垃圾回收前 先从 root 开始 进行递归标记 79 | for(int i = 0;i < root_used;i++) 80 | gc_mark(roots[i].ptr); 81 | //标记完成后 在进行 清除 对于没有标记过的进行回收 82 | gc_sweep(); 83 | } 84 | 85 | -------------------------------------------------------------------------------- /mark-sweep/run.sh: -------------------------------------------------------------------------------- 1 | make && ./gc -------------------------------------------------------------------------------- /mark-sweep/test.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | int clear(){ 3 | free_list = NULL; 4 | for (int i = 0; i <= gc_heaps_used; ++i){ 5 | gc_heaps[i].size = 0; 6 | gc_heaps[i].slot = NULL; 7 | } 8 | gc_heaps_used = 0; 9 | 10 | for (int j = 0; j <= root_used ; ++j){ 11 | roots[j].ptr = NULL; 12 | roots[j].optr = NULL; 13 | } 14 | root_used = 0; 15 | } 16 | /** 17 | * 1. 测试条件保证申请内存保证在TINY_HEAP_SIZE下,因为大于这个数会发生gc 18 | * 测试 分配和 释放是否正常 19 | */ 20 | void test_malloc_free(size_t req_size){ 21 | printf("-----------测试内存申请与释放------------\n"); 22 | printf("-----------***************------------\n"); 23 | //在回收p1的情况下 p2的申请将复用p1的地址 24 | void *p1 = gc_malloc(req_size); 25 | gc_free(p1); 26 | void *p2 = gc_malloc(req_size); 27 | //在上面 p1被释放了,p2 从新申请 会继续从堆的起始位置开始分配 所以 内存地址是一样的 28 | assert(p1 == p2); 29 | gc_free(p2); 30 | 31 | //在不清除p1的情况下 p2 会申请不同内存 32 | p1 = gc_malloc(req_size); 33 | p2 = gc_malloc(req_size); 34 | if(req_size == TINY_HEAP_SIZE) 35 | assert(p1 == p2); 36 | else 37 | assert(p1 != p2); 38 | 39 | 40 | printf("----------- passing ------------\n\n"); 41 | clear(); 42 | } 43 | void test_gc(size_t req_size){ 44 | printf("-----------测试gc ------------\n"); 45 | printf("-----------***************------------\n"); 46 | void *p1 = gc_malloc(req_size); 47 | gc(); 48 | void *p2 = gc_malloc(req_size); 49 | //执行gc后 p1被回收 50 | assert(p1 == p2); 51 | clear(); 52 | 53 | //因为该变量被root引用 所以不会被gc 54 | p1 = gc_malloc(req_size); 55 | add_roots(&p1); 56 | gc(); 57 | p2 = gc_malloc(req_size); 58 | assert(p1 != p2); 59 | 60 | printf("----------- passing ------------\n\n"); 61 | clear(); 62 | } 63 | void test_large_gc(){ 64 | printf("-----------测试大内存gc ------------\n"); 65 | printf("-----------***************------------\n"); 66 | void *p1 = gc_malloc(TINY_HEAP_SIZE); 67 | void *p2 = gc_malloc(TINY_HEAP_SIZE); 68 | //因为 req_size 已经达到堆内存了上限了,且无可用内存 会自动执行gc 69 | assert(p1 == p2); 70 | clear(); 71 | 72 | p1 = gc_malloc(TINY_HEAP_SIZE); 73 | //将对象加入root 74 | add_roots(&p1); 75 | p2 = gc_malloc(TINY_HEAP_SIZE); 76 | //p1 不会被回收 77 | //p2 会申请新的内存 78 | assert(p1 != p2); 79 | 80 | printf("----------- passing ------------\n"); 81 | clear(); 82 | } 83 | 84 | /** 85 | * 对引用进行测试 86 | */ 87 | void test_reference_gc() 88 | { 89 | printf("-----------测试引用gc ------------\n"); 90 | printf("-----------***************------------\n"); 91 | typedef struct obj{ 92 | int v; 93 | struct obj* left; 94 | struct obj* right; 95 | }Obj; 96 | 97 | Obj* p = gc_malloc(sizeof(Obj)); 98 | p->v = 10; 99 | p->left = gc_malloc(sizeof(Obj)); 100 | p->right = gc_malloc(sizeof(Obj)); 101 | p->left->v = 11; 102 | p->right->v = 12; 103 | 104 | //加入root 即使left right 没有加入 但是他们作为 p的子节点引用 会被标记 105 | add_roots(&p); 106 | gc(); 107 | assert(p->v == 10); 108 | assert(p->left->v == 11); 109 | assert(p->right->v == 12); 110 | 111 | p = gc_malloc(sizeof(Obj)); 112 | p->v = 10; 113 | p->left = gc_malloc(sizeof(Obj)); 114 | p->left->v = 11; 115 | Obj* left = p->left; 116 | //没有加入root 会被清除 117 | gc(); 118 | assert(p->v == 0); 119 | assert(p->left == NULL); 120 | //子节点也会被清除 121 | assert(left->v == 0 ); 122 | 123 | 124 | printf("----------- passing ------------\n"); 125 | clear(); 126 | } 127 | /** 128 | * 测试的时候需要关闭 自动gc 129 | */ 130 | void test_malloc_speed(){ 131 | auto_gc = 0; 132 | clock_t start,end; 133 | double duration; 134 | start = clock(); 135 | 136 | // for (int i = 0; i < 1000; ++i) { 137 | // int size = rand()%90; 138 | // void *p = gc_malloc(size); 139 | // } 140 | for (int i = 0; i < 100000; ++i) { 141 | int size = rand()%90; 142 | void *p = gc_malloc(size); 143 | if(!p)abort(); 144 | // gc_free(p); 145 | } 146 | end = clock(); 147 | duration = (double)(end - start)/CLOCKS_PER_SEC; 148 | printf("execution seconds:%f\n",duration); 149 | } 150 | int main(int argc, char **argv) 151 | { 152 | 153 | //小内存测试, 154 | test_malloc_free(8); 155 | clear(); 156 | //大内存测试 157 | test_malloc_free(TINY_HEAP_SIZE); 158 | clear(); 159 | //测试gc 160 | test_gc(8); 161 | clear(); 162 | //大内存 无需手动gc 测试 163 | test_large_gc(); 164 | clear(); 165 | //对象引用测试 166 | test_reference_gc(); 167 | clear(); 168 | 169 | // test_malloc_speed(); 170 | return 0; 171 | } -------------------------------------------------------------------------------- /reame: -------------------------------------------------------------------------------- 1 | 标记清扫: /root/github/project/gc/garbage-collect/mark-sweep -------------------------------------------------------------------------------- /refcount/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc -O2 -pthread $(SRCS) ../gc.c -I ../ -------------------------------------------------------------------------------- /refcount/README.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /refcount/refcount.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include "refcount.h" 6 | #include "gc.h" 7 | void gc(void){ 8 | //nothing need to do for reference-count 9 | } 10 | /** 11 | * 引用计数 + 1 12 | * @param ptr 13 | */ 14 | void gc_inc(void *ptr) 15 | { 16 | //该内存属于哪个堆 17 | GC_Heap *gh; 18 | //该内存的header 19 | Header *hdr; 20 | 21 | //find header 22 | if (!(gh = is_pointer_to_heap(ptr))){ 23 | // printf("not pointer\n"); 24 | return; 25 | } 26 | if (!(hdr = get_header(gh, ptr))) { 27 | printf("not find header\n"); 28 | return; 29 | } 30 | hdr->ref++; 31 | } 32 | /** 33 | * 引用计数- 1 34 | * 如果为0 则回收 35 | * 并对所有的引用进行 -1 36 | * @param ptr 37 | */ 38 | void gc_dec(void *ptr) 39 | { 40 | //该内存属于哪个堆 41 | GC_Heap *gh; 42 | //该内存的header 43 | Header *hdr; 44 | //find header 45 | if (!(gh = is_pointer_to_heap(ptr))){ 46 | // printf("not pointer\n"); 47 | return; 48 | } 49 | if (!(hdr = get_header(gh, ptr))) { 50 | printf("not find header\n"); 51 | return; 52 | } 53 | hdr->ref -- ; 54 | if (hdr->ref == 0) { 55 | //对引用对象进行递归标记 56 | void *p; 57 | void *end = (void*)NEXT_HEADER(hdr); 58 | //对引用进行递归 减引用 59 | for (p = ptr+1; p < end; p++) { 60 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 61 | gc_dec(*(void **)p); 62 | } 63 | //回收 64 | gc_free(ptr); 65 | } 66 | } 67 | /** 68 | * 获取当前的引用计数 69 | * @param ptr 70 | * @return 71 | */ 72 | int ref_count(void *ptr) 73 | { 74 | //该内存属于哪个堆 75 | GC_Heap *gh; 76 | //该内存的header 77 | Header *hdr; 78 | //find header 79 | if (!(gh = is_pointer_to_heap(ptr))){ 80 | // printf("not pointer\n"); 81 | return 0; 82 | } 83 | if (!(hdr = get_header(gh, ptr))) { 84 | printf("not find header\n"); 85 | return 0; 86 | } 87 | return hdr->ref; 88 | } 89 | /** 90 | * 指针赋值操作 91 | * @param ptr 92 | * @param obj 93 | */ 94 | void gc_update(void *ptr,void *obj) 95 | { 96 | gc_inc(obj); 97 | gc_dec(*(void**)ptr); 98 | *(void**)ptr = obj; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /refcount/refcount.h: -------------------------------------------------------------------------------- 1 | #ifndef _REFCOUNT_H 2 | #define _REFCOUNT_H 3 | void gc_inc(void *ptr); 4 | void gc_dec(void *ptr); 5 | void gc_update(void *ptr,void *obj); 6 | int ref_count(void *ptr); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /refcount/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "refcount.h" 7 | #include "gc.h" 8 | 9 | #define ITERS 100000 10 | 11 | typedef struct { 12 | pthread_mutex_t mutex; 13 | int value; 14 | } Data; 15 | /** 16 | * 对value进行递增 17 | * 对于外来变量 在函数前都应该 inc 计数 18 | * 结束后 dec计数 19 | * @param arg 20 | * @return 21 | */ 22 | void *incr(void *arg) 23 | { 24 | gc_inc(arg); 25 | Data* data = (Data*)arg; 26 | 27 | printf("\tincr:引用计数: %d\n", ref_count(data)); 28 | for (int i = 0; i < ITERS; ++i) 29 | { 30 | pthread_mutex_lock(&data->mutex); 31 | data->value++; 32 | pthread_mutex_unlock(&data->mutex); 33 | sched_yield(); 34 | } 35 | gc_dec(data); 36 | printf("\tincr:回收前的引用计数: %d\n", ref_count(data)); 37 | return 0; 38 | } 39 | /** 40 | * 对 value 进行递减 41 | * 对于外来变量 在函数前都应该 inc 计数 42 | * 结束后 dec计数 43 | * @param arg 44 | * @return 45 | */ 46 | void *decr(void *arg) 47 | { 48 | gc_inc(arg); 49 | Data* data = (Data*)arg; 50 | 51 | printf("\tdecr: 引用计数: %d\n", ref_count(data)); 52 | for (int i = ITERS - 1; i >= 0; --i) 53 | { 54 | pthread_mutex_lock(&data->mutex); 55 | data->value--; 56 | pthread_mutex_unlock(&data->mutex); 57 | 58 | //让出cpu 59 | sched_yield(); 60 | } 61 | gc_dec(data); 62 | printf("\tdecr:回收前的引用计数: %d\n", ref_count(data)); 63 | return 0; 64 | } 65 | /** 66 | * 测试多线程gc 67 | * @return 68 | */ 69 | int test_multi_gc(void) 70 | { 71 | 72 | pthread_t incr_thr; 73 | pthread_t decr_thr; 74 | 75 | Data* data = gc_malloc(sizeof(Data)); 76 | if (data == NULL) exit(1); 77 | 78 | printf("gc malloc ref count: %d\n", ref_count(data)); 79 | 80 | data->value = 100; 81 | printf("start value: %d\n", data->value); 82 | 83 | /* create incrementing thread */ 84 | if (pthread_create(&incr_thr, NULL, &incr, data)) { 85 | gc_dec(data); 86 | fprintf(stderr, "Could not create thread\n"); 87 | exit(1); 88 | } 89 | 90 | /* create decrementing thread */ 91 | if (pthread_create(&decr_thr, NULL, &decr, data)) { 92 | gc_dec(data); 93 | fprintf(stderr, "Could not create thread\n"); 94 | exit(1); 95 | } 96 | 97 | /* wait for threads to finish */ 98 | if (pthread_join(incr_thr, NULL)) { 99 | fprintf(stderr, "Could not join\n"); 100 | exit(1); 101 | } 102 | if (pthread_join(decr_thr, NULL)) { 103 | fprintf(stderr, "Could not join\n"); 104 | exit(1); 105 | } 106 | 107 | /* count should be the same as the original */ 108 | printf("end value: %d\n", data->value); 109 | //如果引用技术管理错误 则value不会是100 110 | assert(data->value == 100); 111 | printf("回收前的引用计数: %d\n", ref_count(data)); 112 | //回收 113 | gc_dec(data); 114 | assert(data->value == 0); 115 | 116 | 117 | return 0; 118 | } 119 | /** 120 | * 将多个对象关联起来,只dec其中一个对象 看看是否会被回收 121 | * 实际应该所有的都会被回收 122 | */ 123 | void test_reference(){ 124 | typedef struct t{ 125 | int v; 126 | struct t* left; 127 | struct t* right; 128 | }T; 129 | T* t = gc_malloc(sizeof(T)); 130 | t->right = gc_malloc(sizeof(T)); 131 | t->left = gc_malloc(sizeof(T)); 132 | 133 | T* r = t->right; 134 | T* l = t->left; 135 | r->v = 100; 136 | l->v = 200; 137 | gc_dec(t); 138 | //因为对t进行 -1时 139 | //会递归去寻找相关引用,且 left和right本身只有 1,所以会被回收 140 | assert(r->v == 0); 141 | assert(l->v == 0); 142 | } 143 | void test_reference2(){ 144 | typedef struct t{ 145 | int v; 146 | struct t* left; 147 | struct t* right; 148 | }T; 149 | T* t = gc_malloc(sizeof(T)); 150 | t->right = gc_malloc(sizeof(T)); 151 | t->left = gc_malloc(sizeof(T)); 152 | 153 | //现在 right的引用计数为 2 154 | T* r = t->right; 155 | T* l = t->left; 156 | r->v = 100; 157 | l->v = 200; 158 | //对r进行 ref += 1 159 | gc_inc(r); 160 | 161 | //对t进行 ref -= 1 162 | gc_dec(t); 163 | //因为对t进行 -1时 right引用计数-1 = 1 不会被回收 164 | assert(r->v == 100); 165 | //会递归去寻找相关引用,且 left本身只有 1,所以会被回收 166 | assert(l->v == 0); 167 | } 168 | /** 169 | * 测试更新指针的测试 170 | */ 171 | void test_update_ptr(){ 172 | 173 | typedef struct t { 174 | int v; 175 | struct t *ptr; 176 | }T; 177 | T* a = gc_malloc(sizeof(T)); 178 | T* b = gc_malloc(sizeof(T)); 179 | T* c = gc_malloc(sizeof(T)); 180 | 181 | //现在 a_ref=1 b_ref = 2; 182 | gc_update(&a->ptr,b); 183 | assert(ref_count(b) == 2); 184 | 185 | //现在 a_ref=1 b_ref = 1 c _ref = 2; 186 | gc_update(&a->ptr,c); 187 | assert(ref_count(c) == 2); 188 | assert(ref_count(b) == 1); 189 | 190 | //a_ref = 0 b_ref = 1 c_ref = 1; 191 | gc_dec(a); 192 | assert(ref_count(a) == 0); 193 | assert(ref_count(b) == 1); 194 | assert(ref_count(c) == 1); 195 | 196 | 197 | } 198 | int main(){ 199 | printf("------------测试多线程引用计数---------------\n"); 200 | test_multi_gc(); 201 | printf("----------- passing ---------------\n\n"); 202 | 203 | 204 | printf("------------测试多引用计数1 ---------------\n"); 205 | //测试对象引用 206 | test_reference(); 207 | printf("----------- passing ---------------\n\n"); 208 | 209 | printf("------------测试多引用计数2 ---------------\n"); 210 | //测试对象引用 211 | test_reference2(); 212 | printf("----------- passing ---------------\n\n"); 213 | 214 | 215 | printf("------------测试指针更新计数 ---------------\n"); 216 | //测试对象引用 217 | test_update_ptr(); 218 | printf("----------- passing ---------------\n\n"); 219 | return 0; 220 | } 221 | -------------------------------------------------------------------------------- /root.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /tri-color-marking/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRCS = $(wildcard *.c) 3 | BIN = gc 4 | 5 | all: clean gc 6 | 7 | clean: 8 | rm -f gc a.out 9 | rm -rf *.o 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) ../gc.c -I ../ 13 | 14 | -------------------------------------------------------------------------------- /tri-color-marking/README.md: -------------------------------------------------------------------------------- 1 | [系列实现分析文档](https://wiki.brewlin.com/wiki/blog/gc-learning/GC%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/) -------------------------------------------------------------------------------- /tri-color-marking/stack.c: -------------------------------------------------------------------------------- 1 | #include "stack.h" 2 | 3 | void push(Stack* stk,void* v) 4 | { 5 | Link* node = malloc(sizeof(Link)); 6 | node->value = v; 7 | node->next = NULL; 8 | 9 | if(stk->head == NULL) 10 | stk->head = node; 11 | if(stk->tail == NULL){ 12 | stk->tail = stk->head; 13 | }else{ 14 | stk->tail->next = node; 15 | stk->tail = node; 16 | } 17 | 18 | } 19 | int empty(Stack* stk){ 20 | return stk->head == NULL; 21 | } 22 | void* pop(Stack* stk) 23 | { 24 | if(stk->tail == NULL || stk->head == NULL) 25 | return NULL; 26 | 27 | Link* node = stk->head; 28 | stk->head = node->next; 29 | if(stk->head == NULL) 30 | stk->tail = NULL; 31 | 32 | void* v = node->value; 33 | free(node); 34 | return v; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /tri-color-marking/stack.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName stack 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2020/10/30 0030 上午 10:08 6 | *@Version 1.0 7 | **/ 8 | #ifndef GC_LEARNING_STACK_H 9 | #define GC_LEARNING_STACK_H 10 | 11 | #include 12 | #include 13 | typedef struct link_list 14 | { 15 | void* value; 16 | struct link_list *next; 17 | }Link; 18 | 19 | typedef struct stack_header 20 | { 21 | Link* head; 22 | Link* tail; 23 | }Stack; 24 | 25 | void push(Stack* stk,void* v); 26 | int empty(Stack* stk); 27 | void* pop(Stack* stk); 28 | 29 | #endif //GC_LEARNING_STACK_H 30 | -------------------------------------------------------------------------------- /tri-color-marking/test.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | #include "tri-color.h" 3 | int clear(){ 4 | free_list = NULL; 5 | sweeping = 0; 6 | gc_heaps_used = 0; 7 | root_used = 0; 8 | auto_gc = 1; 9 | } 10 | typedef struct obj{ 11 | int v; 12 | struct obj* left; 13 | struct obj* right; 14 | }Obj; 15 | /** 16 | * 1. 测试条件保证申请内存保证在TINY_HEAP_SIZE下,因为大于这个数会发生gc 17 | * 测试 分配和 释放是否正常 18 | */ 19 | void test_malloc_free(){ 20 | printf("-----------测试内存申请与释放------------\n"); 21 | printf("-----------***************------------\n"); 22 | 23 | //测试的时候关闭自动扩充堆 24 | //关闭自动gc 25 | auto_gc = 0; 26 | auto_grow = 0; 27 | //每个堆 可以存储1个Obj 28 | gc_heaps_used = 1; 29 | gc_init(sizeof(Obj) + HEADER_SIZE); 30 | //在回收p1的情况下 p2的申请将复用p1的地址 31 | Obj *p1 = gc_malloc(sizeof(Obj)); 32 | gc_free(p1); 33 | Obj *p2 = gc_malloc(sizeof(Obj)); 34 | //在上面 p1被释放了,p2 从新申请 会继续从堆的起始位置开始分配 所以 内存地址是一样的 35 | assert(p1 == p2); 36 | 37 | Obj *p3 = gc_malloc(sizeof(Obj)); 38 | assert(p3 == NULL); 39 | 40 | printf("----------- passing ------------\n\n"); 41 | } 42 | void test_gc(){ 43 | printf("-----------测试gc ------------\n"); 44 | printf("-----------***************------------\n"); 45 | 46 | //测试的时候关闭自动扩充堆 47 | auto_grow = 0; 48 | //每个堆 可以存储1个Obj 49 | gc_heaps_used = 3; 50 | gc_init(sizeof(Obj) + HEADER_SIZE); 51 | //一次扫描3个堆 52 | max_sweep = 3; 53 | //一次标记1个对象 54 | max_mark = 1; 55 | 56 | Obj *p1 = gc_malloc(sizeof(Obj)); 57 | add_roots(&p1); 58 | assert(p1); 59 | Obj *p2 = gc_malloc(sizeof(Obj)); 60 | add_roots(&p2); 61 | assert(p2); 62 | Obj *p3 = gc_malloc(sizeof(Obj)); 63 | p3->v = 33; 64 | assert(p3); 65 | 66 | //空间不够 执行gc 67 | Obj *p4 = gc_malloc(sizeof(Obj)); 68 | assert(p4 == NULL); 69 | assert(gc_phase == GC_MARK); 70 | 71 | //需要两次才能标记完 72 | p4 = gc_malloc(sizeof(Obj)); 73 | assert(p4 == NULL); 74 | assert(gc_phase == GC_MARK); 75 | 76 | p4 = gc_malloc(sizeof(Obj)); 77 | assert(p4 == NULL); 78 | assert(gc_phase == GC_SWEEP); 79 | 80 | 81 | //开始执行清除阶段 全部扫描完了 82 | //p3 被释放了 83 | p4 = gc_malloc(sizeof(Obj)); 84 | assert(p3->v == 0); 85 | assert(p4); 86 | assert(gc_phase == GC_ROOT_SCAN); 87 | 88 | printf("----------- passing ------------\n\n"); 89 | } 90 | 91 | /** 92 | * 测试写屏障 93 | */ 94 | void test_write_barrier() 95 | { 96 | printf("-----------测试写屏障 ------------\n"); 97 | printf("-----------***************------------\n"); 98 | //测试的时候关闭自动扩充堆 99 | auto_grow = 0; 100 | auto_gc = 1; 101 | //每个堆 可以存储1个Obj 102 | gc_heaps_used = 1; 103 | gc_init(4 * (sizeof(Obj) + HEADER_SIZE)); 104 | //一次扫描3个堆 105 | max_sweep = 1; 106 | //一次标记1个对象 107 | max_mark = 1; 108 | 109 | Obj* p1 = gc_malloc(sizeof(Obj)); 110 | add_roots(&p1); 111 | //p1 被标记 112 | gc(); 113 | assert(gc_phase == GC_MARK); 114 | 115 | gc(); 116 | assert(gc_phase == GC_SWEEP); 117 | 118 | //接下来就是清除阶段了,但是我在这个阶段的时候 更新了原先的引用 119 | Obj* p2 = gc_malloc(sizeof(Obj)); 120 | p2->v = 22; 121 | write_barrier(p1,&p1->left,p2); 122 | 123 | //清除完毕 124 | gc(); 125 | assert(gc_phase == GC_ROOT_SCAN); 126 | assert(p1->left); 127 | assert(p2->v == 22); 128 | 129 | printf("----------- passing ------------\n"); 130 | clear(); 131 | } 132 | void test_write_barrier2() 133 | { 134 | printf("-----------测试未加入root写屏障 ------------\n"); 135 | printf("-----------***************------------\n"); 136 | //测试的时候关闭自动扩充堆 137 | auto_grow = 0; 138 | auto_gc = 1; 139 | //每个堆 可以存储4个Obj 140 | gc_heaps_used = 1; 141 | gc_init(4 * (sizeof(Obj) + HEADER_SIZE)); 142 | //一次扫描3个堆 143 | max_sweep = 1; 144 | //一次标记1个对象 145 | max_mark = 1; 146 | 147 | Obj* p1 = gc_malloc(sizeof(Obj)); 148 | //p1 被标记 149 | gc(); 150 | assert(gc_phase == GC_MARK); 151 | 152 | gc(); 153 | assert(gc_phase == GC_SWEEP); 154 | 155 | //接下来就是清除阶段了,但是我在这个阶段的时候 更新了原先的引用 156 | Obj* p2 = gc_malloc(sizeof(Obj)); 157 | p2->v = 22; 158 | write_barrier(p1,&p1->left,p2); 159 | 160 | //清除完毕 161 | gc(); 162 | assert(gc_phase == GC_ROOT_SCAN); 163 | assert(p1->left == NULL); 164 | assert(p2->v == 0); 165 | 166 | printf("----------- passing ------------\n"); 167 | clear(); 168 | } 169 | int main(int argc, char **argv) 170 | { 171 | 172 | //小内存测试, 173 | test_malloc_free(); 174 | clear(); 175 | //测试gc 176 | test_gc(); 177 | clear(); 178 | //测试写屏障 179 | test_write_barrier(); 180 | clear(); 181 | //测试未加入root写屏障 182 | test_write_barrier2(); 183 | clear(); 184 | 185 | 186 | return 0; 187 | } -------------------------------------------------------------------------------- /tri-color-marking/tri-color.c: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | #include "stack.h" 3 | #include "tri-color.h" 4 | 5 | Stack stack; 6 | int gc_phase = GC_ROOT_SCAN; 7 | int max_mark = 0; 8 | int max_sweep = 0; 9 | int sweeping = 0; 10 | 11 | /** 12 | * 三色标记是增量式 标记清除算法 所以和标记清除一样的流程 13 | * 为了测试增加了 gc_init函数 自定义初始化堆 14 | */ 15 | void gc_init(size_t heap_size) 16 | { 17 | for (size_t i = 0; i < gc_heaps_used; i++){ 18 | //使用sbrk 向操作系统申请大内存块 19 | void* p = sbrk(heap_size + PTRSIZE); 20 | if(p == NULL)exit(-1); 21 | 22 | gc_heaps[i].slot = (Header *)ALIGN((size_t)p, PTRSIZE); 23 | gc_heaps[i].size = heap_size; 24 | gc_heaps[i].slot->size = heap_size; 25 | gc_heaps[i].slot->next_free = NULL; 26 | 27 | Header* up = gc_heaps[i].slot; 28 | 29 | if(free_list == NULL){ 30 | memset(up + 1,0,up->size - HEADER_SIZE); 31 | free_list = up; 32 | up->flags = 0; 33 | }else{ 34 | gc_free((void *)(up+1)); 35 | } 36 | } 37 | 38 | } 39 | /** 40 | * 对该对象进行标记 41 | * 并进行子对象标记 42 | * @param ptr 43 | */ 44 | void gc_mark(void * ptr) 45 | { 46 | GC_Heap *gh; 47 | Header *hdr; 48 | 49 | /* mark check */ 50 | if (!(gh = is_pointer_to_heap(ptr))){ 51 | // printf("not pointer\n"); 52 | return; 53 | } 54 | if (!(hdr = get_header(gh, ptr))) { 55 | printf("not find header\n"); 56 | return; 57 | } 58 | if (!FL_TEST(hdr, FL_ALLOC)) { 59 | printf("flag not set alloc\n"); 60 | return; 61 | } 62 | if (FL_TEST(hdr, FL_MARK)) { 63 | //printf("flag not set mark\n"); 64 | return; 65 | } 66 | 67 | /* marking */ 68 | FL_SET(hdr, FL_MARK); 69 | //进行子节点 递归 70 | for (void* p = ptr; p < (void*)NEXT_HEADER(hdr); p++) { 71 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 72 | gc_mark(*(void **)p); 73 | } 74 | 75 | } 76 | 77 | /** 78 | * 为了防止在中断gc后 其他操作导致之前gc的状态发生异常 79 | * 需要引入写入屏障,在更新的时候需要调用处理 80 | * @param obj_ptr 81 | * @param field 82 | * @param new_obj_ptr 83 | */ 84 | void write_barrier(void *obj_ptr,void *field,void* new_obj_ptr) 85 | { 86 | 87 | Header* obj = CURRENT_HEADER(obj_ptr); 88 | Header* new_obj = CURRENT_HEADER(new_obj_ptr); 89 | //如果老对象已经被标记了 就要检查新对象是否标记了 90 | if(IS_MARKED(obj)){ 91 | if(!IS_MARKED(new_obj)){ 92 | FL_SET(new_obj,FL_MARK); 93 | push(&stack,new_obj_ptr); 94 | } 95 | } 96 | //obj->field = new_obj 97 | *(void **)field = new_obj_ptr; 98 | 99 | } 100 | /** 101 | * root 扫描阶段 将所有的root可达对象加入队列中 102 | */ 103 | void root_scan_phase() 104 | { 105 | //垃圾回收前 先从 root 开始 进行递归标记 106 | for(int i = 0;i < root_used;i++) 107 | { 108 | void* ptr = roots[i].ptr; 109 | GC_Heap *gh; 110 | Header *hdr; 111 | if (!(gh = is_pointer_to_heap(ptr))) continue; 112 | if (!(hdr = get_header(gh, ptr))) continue; 113 | if (!FL_TEST(hdr, FL_ALLOC)) continue; 114 | if (FL_TEST(hdr, FL_MARK)) continue; 115 | 116 | //标记为灰色 并入栈 117 | FL_SET(hdr, FL_MARK); 118 | push(&stack,ptr); 119 | } 120 | gc_phase = GC_MARK; 121 | 122 | } 123 | /** 124 | * 标记阶段 125 | */ 126 | void mark_phase() 127 | { 128 | //1 全部将灰色标记完了在进行下一个清除阶段 129 | //2 未全部标记完则继续进行标记 130 | 131 | int scan_root = 0; 132 | for (int i = 0; i < max_mark; ++i) { 133 | //如果为空就继续去扫描一下root 看看在gc休息期间是否有新的没有进行标记 134 | if(empty(&stack)){ 135 | //如果扫描过了root,但是依然没有新增灰色对象 则结束标记 136 | if(scan_root >= 1) { 137 | gc_phase = GC_SWEEP; 138 | break; 139 | } 140 | root_scan_phase(); 141 | scan_root++; 142 | continue; 143 | } 144 | void* obj = pop(&stack); 145 | Header* hdr = CURRENT_HEADER(obj); 146 | //递归对child 进行标记 147 | for (void* p = obj; p < (void*)NEXT_HEADER(hdr); p++) { 148 | //对内存解引用,因为内存里面可能存放了内存的地址 也就是引用,需要进行引用的递归标记 149 | gc_mark(*(void **)p); 150 | } 151 | } 152 | //所有gc扫描完以后 只有空栈的话 说明标记完毕 需要进行清扫 153 | if(empty(&stack)){ 154 | gc_phase = GC_SWEEP; 155 | } 156 | 157 | } 158 | /** 159 | * 清除 未标记内存 进行回收利用 160 | */ 161 | void sweep_phase(void) 162 | { 163 | size_t i; 164 | Header *p, *pend, *pnext; 165 | 166 | //遍历所有的堆内存 167 | //因为所有的内存都从堆里申请,所以需要遍历堆找出待回收的内存 168 | for (i = sweeping; i < gc_heaps_used && i < max_sweep + sweeping; i++) { 169 | //pend 堆内存结束为止 170 | pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size); 171 | //堆的起始为止 因为堆的内存可能被分成了很多份,所以需要遍历该堆的内存 172 | for (p = gc_heaps[i].slot; p < pend; p = NEXT_HEADER(p)) { 173 | //查看该堆是否已经被使用 174 | if (FL_TEST(p, FL_ALLOC)) { 175 | //查看该堆是否被标记过 176 | if (FL_TEST(p, FL_MARK)) { 177 | DEBUG(printf("解除标记 : %p\n", p)); 178 | //取消标记,等待下次来回收,如果在下次回收前 179 | //1. 下次回收前发现该内存又被重新访问了,则不需要清除 180 | //2. 下次回收前发现该内存没有被访问过,所以需要清除 181 | FL_UNSET(p, FL_MARK); 182 | }else { 183 | DEBUG(printf("清除回收 :\n")); 184 | gc_free(p+1); 185 | } 186 | } 187 | } 188 | } 189 | 190 | //如果堆扫描完则 切换到root扫描 191 | sweeping = i; 192 | if(i == gc_heaps_used){ 193 | sweeping = 0; 194 | gc_phase = GC_ROOT_SCAN; 195 | } 196 | } 197 | void gc(void) 198 | { 199 | printf("执行gc\n"); 200 | switch(gc_phase){ 201 | case GC_ROOT_SCAN: 202 | root_scan_phase(); 203 | return; 204 | case GC_MARK: 205 | mark_phase(); 206 | return; 207 | case GC_SWEEP: 208 | sweep_phase(); 209 | } 210 | } 211 | 212 | -------------------------------------------------------------------------------- /tri-color-marking/tri-color.h: -------------------------------------------------------------------------------- 1 | /** 2 | *@ClassName tri 3 | *@Deacription go 4 | *@Author brewlin 5 | *@Date 2020/11/3 0003 下午 1:02 6 | *@Version 1.0 7 | **/ 8 | #ifndef GC_LEARNING_TRI_COLOR_H 9 | #define GC_LEARNING_TRI_COLOR_H 10 | 11 | #include "stack.h" 12 | /*** tri-color-marking ***/ 13 | #define GC_ROOT_SCAN 1 14 | #define GC_MARK 2 15 | #define GC_SWEEP 3 16 | 17 | 18 | extern Stack stack; 19 | extern int gc_phase; 20 | extern int max_mark; 21 | extern int max_sweep; 22 | extern int sweeping; 23 | 24 | void write_barrier(void *obj_ptr,void *field,void* new_obj_ptr); 25 | #endif //GC_LEARNING_TRI_COLOR_H 26 | --------------------------------------------------------------------------------