├── MIT-LICENSE ├── Makefile ├── README ├── gc.c └── gc.h /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Narihiro Nakamura 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | CC = gcc 3 | SRCS = gc.c 4 | BIN = gc 5 | 6 | all: clean gc 7 | 8 | clean: 9 | rm -f gc 10 | 11 | gc: $(SRCS) 12 | $(CC) -g -o gc $(SRCS) 13 | 14 | gc_debug: 15 | $(CC) -g -DDO_DEBUG -O0 -o gc $(SRCS) 16 | 17 | test: clean gc_debug 18 | ./gc test 19 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | = mini gc 2 | 3 | = Description 4 | 5 | This is a simple and minimum GC. The purpose of this project is education. 6 | 7 | = Algorithms 8 | 9 | * mark and sweep 10 | 11 | = Usage 12 | 13 | $ make 14 | $ ./gc test 15 | -------------------------------------------------------------------------------- /gc.c: -------------------------------------------------------------------------------- 1 | #ifdef DO_DEBUG 2 | #define DEBUG(exp) (exp) 3 | #else 4 | #define DEBUG(exp) 5 | #endif 6 | 7 | #ifndef DO_DEBUG 8 | #define NDEBUG 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "gc.h" 19 | 20 | /* ========================================================================== */ 21 | /* mini_gc_malloc */ 22 | /* ========================================================================== */ 23 | 24 | typedef struct header { 25 | size_t flags; 26 | size_t size; 27 | struct header *next_free; 28 | } Header; 29 | 30 | typedef struct gc_heap { 31 | Header *slot; 32 | size_t size; 33 | } GC_Heap; 34 | 35 | #define TINY_HEAP_SIZE 0x4000 36 | #define PTRSIZE ((size_t) sizeof(void *)) 37 | #define HEADER_SIZE ((size_t) sizeof(Header)) 38 | #define HEAP_LIMIT 10000 39 | #define ALIGN(x,a) (((x) + (a - 1)) & ~(a - 1)) 40 | #define NEXT_HEADER(x) ((Header *)((size_t)(x+1) + x->size)) 41 | 42 | /* flags */ 43 | #define FL_ALLOC 0x1 44 | #define FL_MARK 0x2 45 | #define FL_SET(x, f) (((Header *)x)->flags |= f) 46 | #define FL_UNSET(x, f) (((Header *)x)->flags &= ~(f)) 47 | #define FL_TEST(x, f) (((Header *)x)->flags & f) 48 | 49 | static Header *free_list; 50 | static GC_Heap gc_heaps[HEAP_LIMIT]; 51 | static size_t gc_heaps_used = 0; 52 | 53 | 54 | static Header * 55 | add_heap(size_t req_size) 56 | { 57 | void *p; 58 | Header *align_p; 59 | 60 | if (gc_heaps_used >= HEAP_LIMIT) { 61 | fputs("OutOfMemory Error", stderr); 62 | abort(); 63 | } 64 | 65 | if (req_size < TINY_HEAP_SIZE) 66 | req_size = TINY_HEAP_SIZE; 67 | 68 | if((p = sbrk(req_size + PTRSIZE + HEADER_SIZE)) == (void *)-1) 69 | return NULL; 70 | 71 | /* address alignment */ 72 | align_p = gc_heaps[gc_heaps_used].slot = (Header *)ALIGN((size_t)p, PTRSIZE); 73 | req_size = gc_heaps[gc_heaps_used].size = req_size; 74 | align_p->size = req_size; 75 | align_p->next_free = align_p; 76 | gc_heaps_used++; 77 | 78 | return align_p; 79 | } 80 | 81 | static Header * 82 | grow(size_t req_size) 83 | { 84 | Header *cp, *up; 85 | 86 | if (!(cp = add_heap(req_size))) 87 | return NULL; 88 | 89 | up = (Header *) cp; 90 | mini_gc_free((void *)(up+1)); 91 | return free_list; 92 | } 93 | 94 | void * 95 | mini_gc_malloc(size_t req_size) 96 | { 97 | Header *p, *prevp; 98 | size_t do_gc = 0; 99 | 100 | req_size = ALIGN(req_size, PTRSIZE); 101 | 102 | if (req_size <= 0) { 103 | return NULL; 104 | } 105 | if ((prevp = free_list) == NULL) { 106 | if (!(p = add_heap(TINY_HEAP_SIZE))) { 107 | return NULL; 108 | } 109 | prevp = free_list = p; 110 | } 111 | for (p = prevp->next_free; ; prevp = p, p = p->next_free) { 112 | if (p->size >= req_size) { 113 | if (p->size == req_size) 114 | /* just fit */ 115 | prevp->next_free = p->next_free; 116 | else { 117 | /* too big */ 118 | p->size -= (req_size + HEADER_SIZE); 119 | p = NEXT_HEADER(p); 120 | p->size = req_size; 121 | } 122 | free_list = prevp; 123 | FL_SET(p, FL_ALLOC); 124 | return (void *)(p+1); 125 | } 126 | if (p == free_list) { 127 | if (!do_gc) { 128 | garbage_collect(); 129 | do_gc = 1; 130 | } 131 | else if ((p = grow(req_size)) == NULL) 132 | return NULL; 133 | } 134 | } 135 | } 136 | 137 | void 138 | mini_gc_free(void *ptr) 139 | { 140 | Header *target, *hit; 141 | 142 | target = (Header *)ptr - 1; 143 | 144 | /* search join point of target to free_list */ 145 | for (hit = free_list; !(target > hit && target < hit->next_free); hit = hit->next_free) 146 | /* heap end? And hit(search)? */ 147 | if (hit >= hit->next_free && 148 | (target > hit || target < hit->next_free)) 149 | break; 150 | 151 | if (NEXT_HEADER(target) == hit->next_free) { 152 | /* merge */ 153 | target->size += (hit->next_free->size + HEADER_SIZE); 154 | target->next_free = hit->next_free->next_free; 155 | } 156 | else { 157 | /* join next free block */ 158 | target->next_free = hit->next_free; 159 | } 160 | if (NEXT_HEADER(hit) == target) { 161 | /* merge */ 162 | hit->size += (target->size + HEADER_SIZE); 163 | hit->next_free = target->next_free; 164 | } 165 | else { 166 | /* join before free block */ 167 | hit->next_free = target; 168 | } 169 | free_list = hit; 170 | target->flags = 0; 171 | } 172 | 173 | 174 | 175 | 176 | /* ========================================================================== */ 177 | /* mini_gc */ 178 | /* ========================================================================== */ 179 | 180 | struct root_range { 181 | void * start; 182 | void * end; 183 | }; 184 | 185 | #define IS_MARKED(x) (FL_TEST(x, FL_ALLOC) && FL_TEST(x, FL_MARK)) 186 | #define ROOT_RANGES_LIMIT 1000 187 | 188 | static struct root_range root_ranges[ROOT_RANGES_LIMIT]; 189 | static size_t root_ranges_used = 0; 190 | static void * stack_start = NULL; 191 | static void * stack_end = NULL; 192 | static GC_Heap *hit_cache = NULL; 193 | 194 | static GC_Heap * 195 | is_pointer_to_heap(void *ptr) 196 | { 197 | size_t i; 198 | 199 | if (hit_cache && 200 | ((void *)hit_cache->slot) <= ptr && 201 | (size_t)ptr < (((size_t)hit_cache->slot) + hit_cache->size)) 202 | return hit_cache; 203 | 204 | for (i = 0; i < gc_heaps_used; i++) { 205 | if ((((void *)gc_heaps[i].slot) <= ptr) && 206 | ((size_t)ptr < (((size_t)gc_heaps[i].slot) + gc_heaps[i].size))) { 207 | hit_cache = &gc_heaps[i]; 208 | return &gc_heaps[i]; 209 | } 210 | } 211 | return NULL; 212 | } 213 | 214 | static Header * 215 | get_header(GC_Heap *gh, void *ptr) 216 | { 217 | Header *p, *pend, *pnext; 218 | 219 | pend = (Header *)(((size_t)gh->slot) + gh->size); 220 | for (p = gh->slot; p < pend; p = pnext) { 221 | pnext = NEXT_HEADER(p); 222 | if ((void *)(p+1) <= ptr && ptr < (void *)pnext) { 223 | return p; 224 | } 225 | } 226 | return NULL; 227 | } 228 | 229 | void 230 | gc_init(void) 231 | { 232 | long dummy; 233 | 234 | /* referenced bdw-gc mark_rts.c */ 235 | dummy = 42; 236 | 237 | /* check stack grow */ 238 | stack_start = ((void *)&dummy); 239 | } 240 | 241 | static void 242 | set_stack_end(void) 243 | { 244 | void *tmp; 245 | long dummy; 246 | 247 | /* referenced bdw-gc mark_rts.c */ 248 | dummy = 42; 249 | 250 | stack_end = (void *)&dummy; 251 | } 252 | 253 | static void gc_mark_range(void *start, void *end); 254 | 255 | static void 256 | gc_mark(void * ptr) 257 | { 258 | GC_Heap *gh; 259 | Header *hdr; 260 | 261 | /* mark check */ 262 | if (!(gh = is_pointer_to_heap(ptr))) return; 263 | if (!(hdr = get_header(gh, ptr))) return; 264 | if (!FL_TEST(hdr, FL_ALLOC)) return; 265 | if (FL_TEST(hdr, FL_MARK)) return; 266 | 267 | /* marking */ 268 | FL_SET(hdr, FL_MARK); 269 | DEBUG(printf("mark ptr : %p, header : %p\n", ptr, hdr)); 270 | 271 | /* mark children */ 272 | gc_mark_range((void *)(hdr+1), (void *)NEXT_HEADER(hdr)); 273 | } 274 | 275 | static void 276 | gc_mark_range(void *start, void *end) 277 | { 278 | void *p; 279 | 280 | for (p = start; p < end; p++) { 281 | gc_mark(*(void **)p); 282 | } 283 | } 284 | 285 | static void 286 | gc_mark_register(void) 287 | { 288 | jmp_buf env; 289 | size_t i; 290 | 291 | setjmp(env); 292 | for (i = 0; i < sizeof(env); i++) { 293 | gc_mark(((void **)env)[i]); 294 | } 295 | } 296 | 297 | static void 298 | gc_mark_stack(void) 299 | { 300 | set_stack_end(); 301 | if (stack_start > stack_end) { 302 | gc_mark_range(stack_end, stack_start); 303 | } 304 | else { 305 | gc_mark_range(stack_start, stack_end); 306 | } 307 | } 308 | 309 | static void 310 | gc_sweep(void) 311 | { 312 | size_t i; 313 | Header *p, *pend, *pnext; 314 | 315 | for (i = 0; i < gc_heaps_used; i++) { 316 | pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size); 317 | for (p = gc_heaps[i].slot; p < pend; p = NEXT_HEADER(p)) { 318 | if (FL_TEST(p, FL_ALLOC)) { 319 | if (FL_TEST(p, FL_MARK)) { 320 | DEBUG(printf("mark unset : %p\n", p)); 321 | FL_UNSET(p, FL_MARK); 322 | } 323 | else { 324 | mini_gc_free(p+1); 325 | } 326 | } 327 | } 328 | } 329 | } 330 | 331 | void 332 | add_roots(void * start, void * end) 333 | { 334 | void *tmp; 335 | if (start > end) { 336 | tmp = start; 337 | start = end; 338 | end = tmp; 339 | } 340 | root_ranges[root_ranges_used].start = start; 341 | root_ranges[root_ranges_used].end = end; 342 | root_ranges_used++; 343 | 344 | if (root_ranges_used >= ROOT_RANGES_LIMIT) { 345 | fputs("Root OverFlow", stderr); 346 | abort(); 347 | } 348 | } 349 | 350 | void 351 | garbage_collect(void) 352 | { 353 | size_t i; 354 | 355 | /* marking machine context */ 356 | gc_mark_register(); 357 | gc_mark_stack(); 358 | 359 | /* marking roots */ 360 | for (i = 0; i < root_ranges_used; i++) { 361 | gc_mark_range(root_ranges[i].start, root_ranges[i].end); 362 | } 363 | 364 | /* sweeping */ 365 | gc_sweep(); 366 | } 367 | 368 | 369 | /* ========================================================================== */ 370 | /* test */ 371 | /* ========================================================================== */ 372 | 373 | static void 374 | test_mini_gc_malloc_free(void) 375 | { 376 | void *p1, *p2, *p3; 377 | 378 | /* malloc check */ 379 | p1 = (void *)mini_gc_malloc(10); 380 | p2 = (void *)mini_gc_malloc(10); 381 | p3 = (void *)mini_gc_malloc(10); 382 | assert(((Header *)p1-1)->size == ALIGN(10, PTRSIZE)); 383 | assert(((Header *)p1-1)->flags == FL_ALLOC); 384 | assert((Header *)(((size_t)(free_list+1)) + free_list->size) == ((Header *)p3-1)); 385 | 386 | /* free check */ 387 | mini_gc_free(p1); 388 | mini_gc_free(p3); 389 | mini_gc_free(p2); 390 | assert(free_list->next_free == free_list); 391 | assert((void *)gc_heaps[0].slot == (void *)free_list); 392 | assert(gc_heaps[0].size == TINY_HEAP_SIZE); 393 | assert(((Header *)p1-1)->flags == 0); 394 | 395 | /* grow check */ 396 | p1 = mini_gc_malloc(TINY_HEAP_SIZE+80); 397 | assert(gc_heaps_used == 2); 398 | assert(gc_heaps[1].size == (TINY_HEAP_SIZE+80)); 399 | mini_gc_free(p1); 400 | } 401 | 402 | static void 403 | test_garbage_collect(void) { 404 | void *p; 405 | p = mini_gc_malloc(100); 406 | assert(FL_TEST((((Header *)p)-1), FL_ALLOC)); 407 | p = 0; 408 | garbage_collect(); 409 | } 410 | 411 | static void 412 | test_garbage_collect_load_test(void) { 413 | void *p; 414 | int i; 415 | for (i = 0; i < 2000; i++) { 416 | p = mini_gc_malloc(100); 417 | } 418 | assert((((Header *)p)-1)->flags); 419 | assert(stack_end != stack_start); 420 | } 421 | 422 | static void 423 | test(void) 424 | { 425 | gc_init(); 426 | test_mini_gc_malloc_free(); 427 | test_garbage_collect(); 428 | test_garbage_collect_load_test(); 429 | } 430 | 431 | 432 | int 433 | main(int argc, char **argv) 434 | { 435 | if (argc == 2 && strcmp(argv[1], "test") == 0) test(); 436 | return 0; 437 | } 438 | -------------------------------------------------------------------------------- /gc.h: -------------------------------------------------------------------------------- 1 | #ifndef _MINI_GC 2 | #define _MINI_GC 3 | 4 | void mini_gc_free(void *ptr); 5 | void * mini_gc_malloc(size_t req_size); 6 | 7 | void garbage_collect(void); 8 | void gc_init(void); 9 | void add_roots(void * start, void * end); 10 | 11 | #endif 12 | 13 | --------------------------------------------------------------------------------