├── .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 |
4 |
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 |
--------------------------------------------------------------------------------