diff --git a/include/mruby/gc.h b/include/mruby/gc.h index 7f40ca64a..ab50e08ad 100644 --- a/include/mruby/gc.h +++ b/include/mruby/gc.h @@ -46,7 +46,8 @@ typedef enum { #endif typedef struct mrb_gc { - struct mrb_heap_page *heaps; /* heaps for GC */ + struct mrb_heap_page *heaps; /* all heaps pages */ + struct mrb_heap_page *free_heaps;/* heaps for allocation */ struct mrb_heap_page *sweeps; /* page where sweep starts */ struct RBasic *gray_list; /* list of gray objects to be traversed incrementally */ struct RBasic *atomic_gray_list; /* list of objects to be traversed atomically */ diff --git a/src/gc.c b/src/gc.c index e5973c46b..566a8ebef 100644 --- a/src/gc.c +++ b/src/gc.c @@ -152,6 +152,8 @@ typedef struct mrb_heap_page { struct RBasic *freelist; struct mrb_heap_page *prev; struct mrb_heap_page *next; + struct mrb_heap_page *free_next; + struct mrb_heap_page *free_prev; mrb_bool old:1; /* Flexible array members area a C99 feature, not C++ compatible */ /* void* objects[]; */ @@ -296,6 +298,51 @@ mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) return is_dead(gc, object); } +static void +link_heap_page(mrb_gc *gc, mrb_heap_page *page) +{ + page->next = gc->heaps; + if (gc->heaps) + gc->heaps->prev = page; + gc->heaps = page; +} + +static void +unlink_heap_page(mrb_gc *gc, mrb_heap_page *page) +{ + if (page->prev) + page->prev->next = page->next; + if (page->next) + page->next->prev = page->prev; + if (gc->heaps == page) + gc->heaps = page->next; + page->prev = NULL; + page->next = NULL; +} + +static void +link_free_heap_page(mrb_gc *gc, mrb_heap_page *page) +{ + page->free_next = gc->free_heaps; + if (gc->free_heaps) { + gc->free_heaps->free_prev = page; + } + gc->free_heaps = page; +} + +static void +unlink_free_heap_page(mrb_gc *gc, mrb_heap_page *page) +{ + if (page->free_prev) + page->free_prev->free_next = page->free_next; + if (page->free_next) + page->free_next->free_prev = page->free_prev; + if (gc->free_heaps == page) + gc->free_heaps = page->free_next; + page->free_prev = NULL; + page->free_next = NULL; +} + static void add_heap(mrb_state *mrb, mrb_gc *gc) { @@ -310,10 +357,8 @@ add_heap(mrb_state *mrb, mrb_gc *gc) } page->freelist = prev; - page->next = gc->heaps; - if (gc->heaps) - gc->heaps->prev = page; - gc->heaps = page; + link_heap_page(gc, page); + link_free_heap_page(gc, page); } #define DEFAULT_GC_INTERVAL_RATIO 200 @@ -334,6 +379,7 @@ mrb_gc_init(mrb_state *mrb, mrb_gc *gc) gc->current_white_part = GC_WHITE_A; gc->heaps = NULL; + gc->free_heaps = NULL; add_heap(mrb, gc); gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO; gc->step_ratio = DEFAULT_GC_STEP_RATIO; @@ -493,20 +539,15 @@ mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls) if (gc->threshold < gc->live) { mrb_incremental_gc(mrb); } - - if (gc->heaps == NULL) { + if (gc->free_heaps == NULL) { add_heap(mrb, gc); } - mrb_heap_page *page = gc->heaps; - while (page->freelist == NULL) { - page = page->next; - if (page == NULL) { - add_heap(mrb, gc); - page = gc->heaps; - } + + struct RBasic *p = gc->free_heaps->freelist; + gc->free_heaps->freelist = ((struct free_obj*)p)->next; + if (gc->free_heaps->freelist == NULL) { + unlink_free_heap_page(gc, gc->free_heaps); } - struct RBasic *p = page->freelist; - page->freelist = ((struct free_obj*)p)->next; gc->live++; gc_protect(mrb, gc, p); @@ -1072,6 +1113,7 @@ incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) RVALUE *e = p + MRB_HEAP_PAGE_SIZE; size_t freed = 0; mrb_bool dead_slot = TRUE; + mrb_bool full = (page->freelist == NULL); if (is_minor_gc(gc) && page->old) { /* skip a slot which doesn't contain any young object */ @@ -1104,15 +1146,15 @@ incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) if (dead_slot) { mrb_heap_page *next = page->next; - if (page->prev) page->prev->next = next; - if (next) next->prev = page->prev; - if (gc->heaps == page) gc->heaps = next; - page->prev = NULL; - page->next = NULL; + unlink_heap_page(gc, page); + unlink_free_heap_page(gc, page); mrb_free(mrb, page); page = next; } else { + if (full && freed > 0) { + link_free_heap_page(gc, page); + } if (page->freelist == NULL && is_minor_gc(gc)) page->old = TRUE; else