GC: adjust cleanup phase to not touch irrelevant page records

Fixes the last place where a minor collection touched a record
per allocated page.
This commit is contained in:
Matthew Flatt 2015-09-10 17:42:46 -06:00
parent 15271b673d
commit 50df879e79

View File

@ -2003,6 +2003,7 @@ inline static void reset_nursery(NewGC *gc)
inline static void move_gen_half_pages_to_old(NewGC *gc)
{
GC_ASSERT(!gc->gen_half.curr_alloc_page);
gc->memory_in_use -= gen_half_size_in_use(gc);
gc->gen_half.old_pages = gc->gen_half.pages;
gc->gen_half.pages = NULL;
}
@ -3614,6 +3615,7 @@ void GC_mark2(const void *const_p, struct NewGC *gc)
if (!work->marked_on) {
work->marked_on = 1;
if (!work->marked_from) {
gc->memory_in_use -= work->size;
work->modified_next = gc->modified_next;
gc->modified_next = work;
}
@ -4210,12 +4212,18 @@ static void reset_gen1_pages_live_and_previous_sizes(NewGC *gc)
static void mark_backpointers(NewGC *gc)
{
/* after this pass, the content of the `modified_next` chain is
pages that have `marked_from` set; additional pages are added
as they acquire `marked_on`, except master-GC pages */
/* For a minor collection, we need to mark any pointers that point
backwards into generation 0, since they're roots.
After this pass, for a minor collection, the content of the
`modified_next` chain is pages that have `marked_from` set.
Additional pages are added as they acquire `marked_on`, except
master-GC pages.
When a generation-1 page is added to the `modified_next` chain,
it's old size is deducted from `memory_is_use`, and its size will
be added back during the repair pahse. */
/* if this is not a full collection, then we need to mark any pointers
that point backwards into generation 0, since they're roots. */
if (!gc->gc_full) {
mpage *work;
int traversed = 0;
@ -4234,6 +4242,7 @@ static void mark_backpointers(NewGC *gc)
/* must be a big page */
work->size_class = SIZE_CLASS_BIG_PAGE_MARKED;
push_ptr(gc, TAG_AS_BIG_PAGE_PTR(BIG_PAGE_TO_OBJECT(work)));
gc->memory_in_use -= work->size;
} else if (work->size_class == SIZE_CLASS_SMALL_PAGE) {
/* small page */
void **start = PAGE_START_VSS(work);
@ -4245,15 +4254,12 @@ static void mark_backpointers(NewGC *gc)
objhead *info = (objhead *)start;
if (!info->dead) {
info->mark = 1;
/* This must be a push_ptr, and not a direct call to
internal_mark. This is because we need every object
in the older heap to be marked out of and noted as
marked before we do anything else */
push_ptr(gc, OBJHEAD_TO_OBJPTR(start));
}
start += info->size;
}
work->previous_size = PREFIX_SIZE;
gc->memory_in_use -= work->size;
} else {
/* medium page */
void **start = PPTR(NUM(work->addr) + PREFIX_SIZE);
@ -4265,11 +4271,12 @@ static void mark_backpointers(NewGC *gc)
objhead *info = (objhead *)start;
if (!info->dead) {
info->mark = 1;
/* This must be a push_ptr (see above) */
push_ptr(gc, OBJHEAD_TO_OBJPTR(info));
}
start += info->size;
}
gc->memory_in_use -= work->live_size;
}
traversed++;
@ -4418,10 +4425,11 @@ inline static void do_heap_compact(NewGC *gc)
}
}
static void repair_mixed_page(NewGC *gc, mpage *page, void **end)
static int repair_mixed_page(NewGC *gc, mpage *page, void **end)
{
void **start = PPTR(NUM(page->addr) + PREFIX_SIZE);
Fixup2_Proc *fixup_table = gc->fixup_table;
int non_dead = 0;
while (start <= end) {
objhead *info = (objhead *)start;
@ -4471,11 +4479,14 @@ static void repair_mixed_page(NewGC *gc, mpage *page, void **end)
abort();
}
info->mark = 0;
non_dead++;
} else {
info->dead = 1;
start += info->size;
}
}
return non_dead;
}
static void chain_marked_on(NewGC *gc)
@ -4509,6 +4520,7 @@ static void chain_marked_on(NewGC *gc)
static void repair_heap(NewGC *gc)
{
uintptr_t memory_in_use;
mpage *page, *next;
Fixup2_Proc *fixup_table = gc->fixup_table;
@ -4520,11 +4532,18 @@ static void repair_heap(NewGC *gc)
page = gc->modified_next;
gc->modified_next = NULL;
memory_in_use = gc->memory_in_use;
for (; page; page = next) {
GC_ASSERT(page->marked_on || page->marked_from);
next = page->modified_next;
page->has_new = 0;
if (gc->gc_full)
page->marked_on = 1;
else
page->marked_on = 0;
page->marked_from = 0;
gc->back_pointers = 0;
if (page->size_class >= SIZE_CLASS_BIG_PAGE) {
void **start = PPTR(BIG_PAGE_TO_OBJECT(page));
@ -4557,6 +4576,8 @@ static void repair_heap(NewGC *gc)
break;
}
}
memory_in_use += page->size;
} else if (page->size_class == SIZE_CLASS_SMALL_PAGE) {
void **start = PPTR(NUM(page->addr) + page->previous_size);
void **end = PAGE_END_VSS(page);
@ -4638,9 +4659,21 @@ static void repair_heap(NewGC *gc)
}
break;
}
page->previous_size = page->size;
memory_in_use += page->size;
} else {
int non_dead;
GC_ASSERT(page->size_class == SIZE_CLASS_MED_PAGE);
repair_mixed_page(gc, page, PPTR(NUM(page->addr) + APAGE_SIZE - page->size));
non_dead = repair_mixed_page(gc, page, PPTR(NUM(page->addr) + APAGE_SIZE - page->size));
page->live_size = (page->size * non_dead);
memory_in_use += page->live_size;
page->previous_size = PREFIX_SIZE; /* start next block search at the beginning */
if (page->generation == AGE_GEN_0) {
/* Tell the clean-up phase to keep this one (even for a minor GC): */
page->marked_on = 1;
GC_ASSERT(non_dead); /* otherwise, wouldn't have marked_on */
}
}
/* If there was a reference to a generation 1/2 object, that's a
@ -4654,8 +4687,12 @@ static void repair_heap(NewGC *gc)
/* All gen-half pages count as modified: */
for (page = gc->gen_half.pages; page; page = page->next) {
GC_ASSERT(page->generation == AGE_GEN_HALF);
repair_mixed_page(gc, page, PPTR(NUM(page->addr) + page->size - 1));
(void)repair_mixed_page(gc, page, PPTR(NUM(page->addr) + page->size - 1));
}
memory_in_use += gen_half_size_in_use(gc);
memory_in_use = add_no_overflow(memory_in_use, gc->phantom_count);
gc->memory_in_use = memory_in_use;
}
static inline void gen1_free_mpage(PageMap pagemap, mpage *page) {
@ -4730,8 +4767,8 @@ static void clean_up_heap(NewGC *gc)
gen0_free_big_pages(gc);
for(i = 0; i < PAGE_TYPES; i++) {
if (gc->gc_full) {
if (gc->gc_full) {
for (i = 0; i < PAGE_TYPES; i++) {
mpage *work = gc->gen1_pages[i];
mpage *prev = NULL;
while(work) {
@ -4746,80 +4783,54 @@ static void clean_up_heap(NewGC *gc)
} else {
GCVERBOSEPAGE(gc, "clean_up_heap BIG PAGE ALIVE", work);
work->marked_on = 0;
work->marked_from = 0;
work->previous_size = work->size;
memory_in_use += work->size;
prev = work;
}
work = next;
}
} else {
mpage *work;
for (work = gc->gen1_pages[i]; work; work = work->next) {
work->marked_on = 0;
work->marked_from = 0;
work->previous_size = work->size;
memory_in_use += work->size;
}
}
}
/* For medium pages, generation-0 pages will appear first in each
list, so for a mnior GC, we can stop whenever we find a
generation-1 page */
for (ty = 0; ty < MED_PAGE_TYPES; ty++) {
for (i = 0; i < NUM_MED_PAGE_SIZES; i++) {
mpage *work;
mpage *prev = NULL, *next;
for (work = gc->med_pages[ty][i]; work; work = next) {
next = work->next;
if (work->marked_on) {
void **start = PPTR(NUM(work->addr) + PREFIX_SIZE);
void **end = PPTR(NUM(work->addr) + APAGE_SIZE - work->size);
int non_dead = 0;
while(start <= end) {
objhead *info = (objhead *)start;
if (!info->dead) {
non_dead++;
}
start += info->size;
}
GC_ASSERT(non_dead); /* otherwise, wouldn't have marked_on */
next = work->next;
work->live_size = (work->size * non_dead);
memory_in_use += work->live_size;
work->previous_size = PREFIX_SIZE;
work->marked_on = 0;
work->marked_from = 0;
memory_in_use += work->live_size;
work->generation = AGE_GEN_1;
prev = work;
} else if (gc->gc_full || (work->generation == AGE_GEN_0)) {
/* Page wasn't touched in full GC, or gen-0 not touched,
so we can free it. */
next = work->next;
if(prev) prev->next = next; else gc->med_pages[ty][i] = next;
if(next) work->next->prev = prev;
GCVERBOSEPAGE(gc, "Cleaning up MED NO MARKEDON", work);
gen1_free_mpage(pagemap, work);
--gc->num_gen1_pages;
} else {
/* not marked during minor gc */
memory_in_use += work->live_size;
work->previous_size = PREFIX_SIZE;
work->marked_from = 0;
next = work->next;
prev = work;
/* no more marked during minor gc */
next = NULL;
}
}
gc->med_freelist_pages[ty][i] = prev;
}
}
memory_in_use += gen_half_size_in_use(gc);
if (gc->gc_full) {
memory_in_use += gen_half_size_in_use(gc);
memory_in_use = add_no_overflow(memory_in_use, gc->phantom_count);
memory_in_use = add_no_overflow(memory_in_use, gc->phantom_count);
gc->memory_in_use = memory_in_use;
}
gc->memory_in_use = memory_in_use;
cleanup_vacated_pages(gc);
}
@ -5032,6 +5043,10 @@ static void garbage_collect(NewGC *gc, int force_full, int no_full, int switchin
if (gc->gc_full)
gc->phantom_count = 0;
else if (gc->memory_in_use > gc->phantom_count) {
/* added back in repair_heap(), after any adjustments from gen0 phantom bytes */
gc->memory_in_use -= gc->phantom_count;
}
gc->gen0_phantom_count = 0;
TIME_INIT();