Clean up some GC logic and fix a bug where large object allocation could grow the heap unnecessarily
parent
b13eb201bd
commit
83b8717bad
|
@ -14,10 +14,8 @@ void factor_vm::collect_aging()
|
||||||
/* Promote objects referenced from tenured space to tenured space, copy
|
/* Promote objects referenced from tenured space to tenured space, copy
|
||||||
everything else to the aging semi-space, and reset the nursery pointer. */
|
everything else to the aging semi-space, and reset the nursery pointer. */
|
||||||
{
|
{
|
||||||
/* Change the op so that if we fail here, we proceed to a full
|
/* Change the op so that if we fail here, an assertion will be
|
||||||
tenured collection. We are collecting to tenured space, and
|
raised. */
|
||||||
cards were unmarked, so we can't proceed with a to_tenured
|
|
||||||
collection. */
|
|
||||||
current_gc->op = collect_to_tenured_op;
|
current_gc->op = collect_to_tenured_op;
|
||||||
|
|
||||||
to_tenured_collector collector(this);
|
to_tenured_collector collector(this);
|
||||||
|
|
|
@ -330,6 +330,14 @@ void factor_vm::collect_compact(bool trace_contexts_p)
|
||||||
{
|
{
|
||||||
collect_mark_impl(trace_contexts_p);
|
collect_mark_impl(trace_contexts_p);
|
||||||
collect_compact_impl(trace_contexts_p);
|
collect_compact_impl(trace_contexts_p);
|
||||||
|
|
||||||
|
if(data->high_fragmentation_p())
|
||||||
|
{
|
||||||
|
/* Compaction did not free up enough memory. Grow the heap. */
|
||||||
|
set_current_gc_op(collect_growing_heap_op);
|
||||||
|
collect_growing_heap(0,trace_contexts_p);
|
||||||
|
}
|
||||||
|
|
||||||
code->flush_icache();
|
code->flush_icache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,12 +100,12 @@ void data_heap::reset_generation(tenured_space *gen)
|
||||||
|
|
||||||
bool data_heap::high_fragmentation_p()
|
bool data_heap::high_fragmentation_p()
|
||||||
{
|
{
|
||||||
return (tenured->largest_free_block() <= nursery->size + aging->size);
|
return (tenured->largest_free_block() <= high_water_mark());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool data_heap::low_memory_p()
|
bool data_heap::low_memory_p()
|
||||||
{
|
{
|
||||||
return (tenured->free_space() <= nursery->size + aging->size);
|
return (tenured->free_space() <= high_water_mark());
|
||||||
}
|
}
|
||||||
|
|
||||||
void data_heap::mark_all_cards()
|
void data_heap::mark_all_cards()
|
||||||
|
|
|
@ -32,6 +32,9 @@ struct data_heap {
|
||||||
bool high_fragmentation_p();
|
bool high_fragmentation_p();
|
||||||
bool low_memory_p();
|
bool low_memory_p();
|
||||||
void mark_all_cards();
|
void mark_all_cards();
|
||||||
|
cell high_water_mark() {
|
||||||
|
return nursery->size + aging->size;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct data_heap_room {
|
struct data_heap_room {
|
||||||
|
|
|
@ -112,11 +112,14 @@ void factor_vm::collect_full(bool trace_contexts_p)
|
||||||
|
|
||||||
if(data->low_memory_p())
|
if(data->low_memory_p())
|
||||||
{
|
{
|
||||||
|
/* Full GC did not free up enough memory. Grow the heap. */
|
||||||
set_current_gc_op(collect_growing_heap_op);
|
set_current_gc_op(collect_growing_heap_op);
|
||||||
collect_growing_heap(0,trace_contexts_p);
|
collect_growing_heap(0,trace_contexts_p);
|
||||||
}
|
}
|
||||||
else if(data->high_fragmentation_p())
|
else if(data->high_fragmentation_p())
|
||||||
{
|
{
|
||||||
|
/* Enough free memory, but it is not contiguous. Perform a
|
||||||
|
compaction. */
|
||||||
set_current_gc_op(collect_compact_op);
|
set_current_gc_op(collect_compact_op);
|
||||||
collect_compact_impl(trace_contexts_p);
|
collect_compact_impl(trace_contexts_p);
|
||||||
}
|
}
|
||||||
|
|
37
vm/gc.cpp
37
vm/gc.cpp
|
@ -116,19 +116,19 @@ void factor_vm::start_gc_again()
|
||||||
switch(current_gc->op)
|
switch(current_gc->op)
|
||||||
{
|
{
|
||||||
case collect_nursery_op:
|
case collect_nursery_op:
|
||||||
|
/* Nursery collection can fail if aging does not have enough
|
||||||
|
free space to fit all live objects from nursery. */
|
||||||
current_gc->op = collect_aging_op;
|
current_gc->op = collect_aging_op;
|
||||||
break;
|
break;
|
||||||
case collect_aging_op:
|
case collect_aging_op:
|
||||||
|
/* Aging collection can fail if the aging semispace cannot fit
|
||||||
|
all the live objects from the other aging semispace and the
|
||||||
|
nursery. */
|
||||||
current_gc->op = collect_to_tenured_op;
|
current_gc->op = collect_to_tenured_op;
|
||||||
break;
|
break;
|
||||||
case collect_to_tenured_op:
|
|
||||||
current_gc->op = collect_full_op;
|
|
||||||
break;
|
|
||||||
case collect_full_op:
|
|
||||||
case collect_compact_op:
|
|
||||||
current_gc->op = collect_growing_heap_op;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
|
/* Nothing else should fail mid-collection due to insufficient
|
||||||
|
space in the target generation. */
|
||||||
critical_error("Bad GC op",current_gc->op);
|
critical_error("Bad GC op",current_gc->op);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -148,10 +148,16 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p)
|
||||||
assert(!gc_off);
|
assert(!gc_off);
|
||||||
assert(!current_gc);
|
assert(!current_gc);
|
||||||
|
|
||||||
|
/* Important invariant: tenured space must have enough contiguous free
|
||||||
|
space to fit the entire contents of the aging space and nursery. This is
|
||||||
|
because when doing a full collection, objects from younger generations
|
||||||
|
are promoted before any unreachable tenured objects are freed. */
|
||||||
|
assert(!data->high_fragmentation_p());
|
||||||
|
|
||||||
current_gc = new gc_state(op,this);
|
current_gc = new gc_state(op,this);
|
||||||
|
|
||||||
/* Keep trying to GC higher and higher generations until we don't run out
|
/* Keep trying to GC higher and higher generations until we don't run
|
||||||
of space */
|
out of space in the target generation. */
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -164,17 +170,23 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p)
|
||||||
collect_nursery();
|
collect_nursery();
|
||||||
break;
|
break;
|
||||||
case collect_aging_op:
|
case collect_aging_op:
|
||||||
|
/* We end up here if the above fails. */
|
||||||
collect_aging();
|
collect_aging();
|
||||||
if(data->high_fragmentation_p())
|
if(data->high_fragmentation_p())
|
||||||
{
|
{
|
||||||
|
/* Change GC op so that if we fail again,
|
||||||
|
we crash. */
|
||||||
set_current_gc_op(collect_full_op);
|
set_current_gc_op(collect_full_op);
|
||||||
collect_full(trace_contexts_p);
|
collect_full(trace_contexts_p);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case collect_to_tenured_op:
|
case collect_to_tenured_op:
|
||||||
|
/* We end up here if the above fails. */
|
||||||
collect_to_tenured();
|
collect_to_tenured();
|
||||||
if(data->high_fragmentation_p())
|
if(data->high_fragmentation_p())
|
||||||
{
|
{
|
||||||
|
/* Change GC op so that if we fail again,
|
||||||
|
we crash. */
|
||||||
set_current_gc_op(collect_full_op);
|
set_current_gc_op(collect_full_op);
|
||||||
collect_full(trace_contexts_p);
|
collect_full(trace_contexts_p);
|
||||||
}
|
}
|
||||||
|
@ -197,7 +209,7 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p)
|
||||||
}
|
}
|
||||||
catch(const must_start_gc_again &)
|
catch(const must_start_gc_again &)
|
||||||
{
|
{
|
||||||
/* We come back here if a generation is full */
|
/* We come back here if the target generation is full. */
|
||||||
start_gc_again();
|
start_gc_again();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -207,6 +219,9 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p)
|
||||||
|
|
||||||
delete current_gc;
|
delete current_gc;
|
||||||
current_gc = NULL;
|
current_gc = NULL;
|
||||||
|
|
||||||
|
/* Check the invariant again, just in case. */
|
||||||
|
assert(!data->high_fragmentation_p());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* primitive_minor_gc() is invoked by inline GC checks, and it needs to fill in
|
/* primitive_minor_gc() is invoked by inline GC checks, and it needs to fill in
|
||||||
|
@ -283,7 +298,7 @@ void factor_vm::primitive_compact_gc()
|
||||||
object *factor_vm::allot_large_object(cell type, cell size)
|
object *factor_vm::allot_large_object(cell type, cell size)
|
||||||
{
|
{
|
||||||
/* If tenured space does not have enough room, collect and compact */
|
/* If tenured space does not have enough room, collect and compact */
|
||||||
if(!data->tenured->can_allot_p(size))
|
if(!data->tenured->can_allot_p(size + data->high_water_mark()))
|
||||||
{
|
{
|
||||||
primitive_compact_gc();
|
primitive_compact_gc();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue