vm: during tenuring stage of aging collection, if tenured space fills up, it would attempt a to_tenured collection. this will succeed if all roots were tenured. however, this is unsound, because there's now an untraced segment of tenured space. fix: if tenuring fails, go on to do a full collection instead
parent
15e4f08d78
commit
45eb68fa38
|
@ -4,12 +4,21 @@ namespace factor
|
|||
{
|
||||
|
||||
aging_collector::aging_collector(factor_vm *myvm_) :
|
||||
copying_collector<aging_space,aging_policy>
|
||||
(myvm_,myvm_->data->aging,aging_policy(myvm_)) {}
|
||||
copying_collector<aging_space,aging_policy>(
|
||||
myvm_,
|
||||
&myvm_->gc_stats.aging_stats,
|
||||
myvm_->data->aging,
|
||||
aging_policy(myvm_)) {}
|
||||
|
||||
void factor_vm::collect_aging()
|
||||
{
|
||||
{
|
||||
/* Change the op so that if we fail here, we proceed to a full
|
||||
tenured collection. We are collecting to tenured space, and
|
||||
cards were unmarked, so we can't proceed with a to_tenured
|
||||
collection. */
|
||||
current_gc->op = collect_to_tenured_op;
|
||||
|
||||
to_tenured_collector collector(this);
|
||||
collector.trace_cards(data->tenured,
|
||||
card_points_to_aging,
|
||||
|
@ -17,6 +26,9 @@ void factor_vm::collect_aging()
|
|||
collector.cheneys_algorithm();
|
||||
}
|
||||
{
|
||||
/* If collection fails here, do a to_tenured collection. */
|
||||
current_gc->op = collect_aging_op;
|
||||
|
||||
std::swap(data->aging,data->aging_semispace);
|
||||
reset_generation(data->aging);
|
||||
|
||||
|
|
|
@ -229,7 +229,7 @@ critical here */
|
|||
void factor_vm::compact_code_heap()
|
||||
{
|
||||
/* Free all unreachable code blocks, don't trace contexts */
|
||||
garbage_collection(tenured_gen,false,false,0);
|
||||
garbage_collection(collect_full_op,false,0);
|
||||
|
||||
/* Figure out where the code heap blocks are going to end up */
|
||||
cell size = code->compute_heap_forwarding();
|
||||
|
|
|
@ -6,14 +6,16 @@ template<typename TargetGeneration, typename Policy> struct collector {
|
|||
data_heap *data;
|
||||
code_heap *code;
|
||||
gc_state *current_gc;
|
||||
generation_statistics *stats;
|
||||
TargetGeneration *target;
|
||||
Policy policy;
|
||||
|
||||
explicit collector(factor_vm *myvm_, TargetGeneration *target_, Policy policy_) :
|
||||
explicit collector(factor_vm *myvm_, generation_statistics *stats_, TargetGeneration *target_, Policy policy_) :
|
||||
myvm(myvm_),
|
||||
data(myvm_->data),
|
||||
code(myvm_->code),
|
||||
current_gc(myvm_->current_gc),
|
||||
stats(stats_),
|
||||
target(target_),
|
||||
policy(policy_) {}
|
||||
|
||||
|
@ -74,7 +76,6 @@ template<typename TargetGeneration, typename Policy> struct collector {
|
|||
memcpy(newpointer,untagged,size);
|
||||
untagged->h.forward_to(newpointer);
|
||||
|
||||
generation_statistics *stats = &myvm->gc_stats.generations[current_gc->collecting_gen];
|
||||
stats->object_count++;
|
||||
stats->bytes_copied += size;
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ template<typename TargetGeneration, typename Policy>
|
|||
struct copying_collector : collector<TargetGeneration,Policy> {
|
||||
cell scan;
|
||||
|
||||
explicit copying_collector(factor_vm *myvm_, TargetGeneration *target_, Policy policy_) :
|
||||
collector<TargetGeneration,Policy>(myvm_,target_,policy_), scan(target_->here) {}
|
||||
explicit copying_collector(factor_vm *myvm_, generation_statistics *stats_, TargetGeneration *target_, Policy policy_) :
|
||||
collector<TargetGeneration,Policy>(myvm_,stats_,target_,policy_), scan(target_->here) {}
|
||||
|
||||
inline cell first_card_in_deck(cell deck)
|
||||
{
|
||||
|
@ -28,11 +28,6 @@ struct copying_collector : collector<TargetGeneration,Policy> {
|
|||
return first_card_in_deck(deck + 1);
|
||||
}
|
||||
|
||||
inline cell card_to_addr(cell c)
|
||||
{
|
||||
return c << card_bits + this->data->start;
|
||||
}
|
||||
|
||||
inline cell card_deck_for_address(cell a)
|
||||
{
|
||||
return addr_to_deck(a - this->data->start);
|
||||
|
|
|
@ -27,9 +27,4 @@ struct data_heap {
|
|||
data_heap *grow(cell requested_size);
|
||||
};
|
||||
|
||||
static const cell nursery_gen = 0;
|
||||
static const cell aging_gen = 1;
|
||||
static const cell tenured_gen = 2;
|
||||
static const cell gen_count = 3;
|
||||
|
||||
}
|
||||
|
|
10
vm/debug.cpp
10
vm/debug.cpp
|
@ -211,9 +211,9 @@ void factor_vm::dump_memory(cell from, cell to)
|
|||
dump_cell(from);
|
||||
}
|
||||
|
||||
void factor_vm::dump_zone(cell gen, zone *z)
|
||||
void factor_vm::dump_zone(char *name, zone *z)
|
||||
{
|
||||
print_string("Generation "); print_cell(gen); print_string(": ");
|
||||
print_string(name); print_string(": ");
|
||||
print_string("Start="); print_cell(z->start);
|
||||
print_string(", size="); print_cell(z->size);
|
||||
print_string(", here="); print_cell(z->here - z->start); nl();
|
||||
|
@ -221,9 +221,9 @@ void factor_vm::dump_zone(cell gen, zone *z)
|
|||
|
||||
void factor_vm::dump_generations()
|
||||
{
|
||||
dump_zone(nursery_gen,&nursery);
|
||||
dump_zone(aging_gen,data->aging);
|
||||
dump_zone(tenured_gen,data->tenured);
|
||||
dump_zone("Nursery",&nursery);
|
||||
dump_zone("Aging",data->aging);
|
||||
dump_zone("Tenured",data->tenured);
|
||||
|
||||
print_string("Cards: base=");
|
||||
print_cell((cell)data->cards);
|
||||
|
|
|
@ -4,7 +4,11 @@ namespace factor
|
|||
{
|
||||
|
||||
full_collector::full_collector(factor_vm *myvm_) :
|
||||
copying_collector<tenured_space,full_policy>(myvm_,myvm_->data->tenured,full_policy(myvm_)) {}
|
||||
copying_collector<tenured_space,full_policy>(
|
||||
myvm_,
|
||||
&myvm_->gc_stats.full_stats,
|
||||
myvm_->data->tenured,
|
||||
full_policy(myvm_)) {}
|
||||
|
||||
struct stack_frame_marker {
|
||||
factor_vm *myvm;
|
||||
|
|
137
vm/gc.cpp
137
vm/gc.cpp
|
@ -3,14 +3,9 @@
|
|||
namespace factor
|
||||
{
|
||||
|
||||
gc_state::gc_state(data_heap *data_, bool growing_data_heap_, cell collecting_gen_) :
|
||||
data(data_),
|
||||
growing_data_heap(growing_data_heap_),
|
||||
collecting_gen(collecting_gen_),
|
||||
collecting_aging_again(false),
|
||||
start_time(current_micros()) { }
|
||||
gc_state::gc_state(gc_op op_) : op(op_), start_time(current_micros()) {}
|
||||
|
||||
gc_state::~gc_state() { }
|
||||
gc_state::~gc_state() {}
|
||||
|
||||
void factor_vm::update_dirty_code_blocks(std::set<code_block *> *remembered_set)
|
||||
{
|
||||
|
@ -21,80 +16,81 @@ void factor_vm::update_dirty_code_blocks(std::set<code_block *> *remembered_set)
|
|||
for(; iter != end; iter++) update_literal_references(*iter);
|
||||
}
|
||||
|
||||
void factor_vm::record_gc_stats()
|
||||
void factor_vm::record_gc_stats(generation_statistics *stats)
|
||||
{
|
||||
generation_statistics *s = &gc_stats.generations[current_gc->collecting_gen];
|
||||
|
||||
cell gc_elapsed = (current_micros() - current_gc->start_time);
|
||||
s->collections++;
|
||||
s->gc_time += gc_elapsed;
|
||||
if(s->max_gc_time < gc_elapsed)
|
||||
s->max_gc_time = gc_elapsed;
|
||||
stats->collections++;
|
||||
stats->gc_time += gc_elapsed;
|
||||
if(stats->max_gc_time < gc_elapsed)
|
||||
stats->max_gc_time = gc_elapsed;
|
||||
}
|
||||
|
||||
/* Collect gen and all younger generations.
|
||||
If growing_data_heap_ is true, we must grow the data heap to such a size that
|
||||
an allocation of requested_bytes won't fail */
|
||||
void factor_vm::garbage_collection(cell collecting_gen_, bool growing_data_heap_, bool trace_contexts_p, cell requested_bytes)
|
||||
void factor_vm::garbage_collection(gc_op op, bool trace_contexts_p, cell requested_bytes)
|
||||
{
|
||||
assert(!gc_off);
|
||||
assert(!current_gc);
|
||||
|
||||
save_stacks();
|
||||
|
||||
current_gc = new gc_state(data,growing_data_heap_,collecting_gen_);
|
||||
current_gc = new gc_state(op);
|
||||
|
||||
/* Keep trying to GC higher and higher generations until we don't run out
|
||||
of space */
|
||||
if(setjmp(current_gc->gc_unwind))
|
||||
{
|
||||
/* We come back here if a generation is full */
|
||||
|
||||
/* We have no older generations we can try collecting, so we
|
||||
resort to growing the data heap */
|
||||
if(current_gc->collecting_tenured_p())
|
||||
switch(current_gc->op)
|
||||
{
|
||||
assert(!current_gc->growing_data_heap);
|
||||
current_gc->growing_data_heap = true;
|
||||
|
||||
case collect_nursery_op:
|
||||
current_gc->op = collect_aging_op;
|
||||
break;
|
||||
case collect_aging_op:
|
||||
current_gc->op = collect_to_tenured_op;
|
||||
break;
|
||||
case collect_to_tenured_op:
|
||||
current_gc->op = collect_full_op;
|
||||
break;
|
||||
case collect_full_op:
|
||||
/* Since we start tracing again, any previously
|
||||
marked code blocks must be re-marked and re-traced */
|
||||
code->clear_mark_bits();
|
||||
}
|
||||
/* we try collecting aging space twice before going on to
|
||||
collect tenured */
|
||||
else if(current_gc->collecting_aging_p()
|
||||
&& !current_gc->collecting_aging_again)
|
||||
{
|
||||
current_gc->collecting_aging_again = true;
|
||||
}
|
||||
/* Collect the next oldest generation */
|
||||
else
|
||||
{
|
||||
current_gc->collecting_gen++;
|
||||
current_gc->op = collect_growing_heap_op;
|
||||
break;
|
||||
default:
|
||||
critical_error("Bad GC op\n",op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(current_gc->collecting_nursery_p())
|
||||
switch(current_gc->op)
|
||||
{
|
||||
case collect_nursery_op:
|
||||
collect_nursery();
|
||||
else if(current_gc->collecting_aging_p())
|
||||
{
|
||||
if(current_gc->collecting_aging_again)
|
||||
collect_to_tenured();
|
||||
else
|
||||
record_gc_stats(&gc_stats.nursery_stats);
|
||||
break;
|
||||
case collect_aging_op:
|
||||
collect_aging();
|
||||
}
|
||||
else if(current_gc->collecting_tenured_p())
|
||||
{
|
||||
if(current_gc->growing_data_heap)
|
||||
collect_growing_heap(requested_bytes,trace_contexts_p);
|
||||
else
|
||||
record_gc_stats(&gc_stats.aging_stats);
|
||||
break;
|
||||
case collect_to_tenured_op:
|
||||
collect_to_tenured();
|
||||
record_gc_stats(&gc_stats.aging_stats);
|
||||
break;
|
||||
case collect_full_op:
|
||||
collect_full(trace_contexts_p);
|
||||
record_gc_stats(&gc_stats.full_stats);
|
||||
break;
|
||||
case collect_growing_heap_op:
|
||||
collect_growing_heap(requested_bytes,trace_contexts_p);
|
||||
record_gc_stats(&gc_stats.full_stats);
|
||||
break;
|
||||
default:
|
||||
critical_error("Bad GC op\n",op);
|
||||
break;
|
||||
}
|
||||
else
|
||||
critical_error("Bug in GC",0);
|
||||
|
||||
record_gc_stats();
|
||||
|
||||
delete current_gc;
|
||||
current_gc = NULL;
|
||||
|
@ -102,7 +98,7 @@ void factor_vm::garbage_collection(cell collecting_gen_, bool growing_data_heap_
|
|||
|
||||
void factor_vm::gc()
|
||||
{
|
||||
garbage_collection(tenured_gen,false,true,0);
|
||||
garbage_collection(collect_full_op,true,0);
|
||||
}
|
||||
|
||||
void factor_vm::primitive_gc()
|
||||
|
@ -110,25 +106,28 @@ void factor_vm::primitive_gc()
|
|||
gc();
|
||||
}
|
||||
|
||||
void factor_vm::add_gc_stats(generation_statistics *stats, growable_array *result)
|
||||
{
|
||||
result->add(allot_cell(stats->collections));
|
||||
result->add(tag<bignum>(long_long_to_bignum(stats->gc_time)));
|
||||
result->add(tag<bignum>(long_long_to_bignum(stats->max_gc_time)));
|
||||
result->add(allot_cell(stats->collections == 0 ? 0 : stats->gc_time / stats->collections));
|
||||
result->add(allot_cell(stats->object_count));
|
||||
result->add(tag<bignum>(long_long_to_bignum(stats->bytes_copied)));
|
||||
}
|
||||
|
||||
void factor_vm::primitive_gc_stats()
|
||||
{
|
||||
growable_array result(this);
|
||||
|
||||
cell i;
|
||||
u64 total_gc_time = 0;
|
||||
add_gc_stats(&gc_stats.nursery_stats,&result);
|
||||
add_gc_stats(&gc_stats.aging_stats,&result);
|
||||
add_gc_stats(&gc_stats.full_stats,&result);
|
||||
|
||||
for(i = 0; i < gen_count; i++)
|
||||
{
|
||||
generation_statistics *s = &gc_stats.generations[i];
|
||||
result.add(allot_cell(s->collections));
|
||||
result.add(tag<bignum>(long_long_to_bignum(s->gc_time)));
|
||||
result.add(tag<bignum>(long_long_to_bignum(s->max_gc_time)));
|
||||
result.add(allot_cell(s->collections == 0 ? 0 : s->gc_time / s->collections));
|
||||
result.add(allot_cell(s->object_count));
|
||||
result.add(tag<bignum>(long_long_to_bignum(s->bytes_copied)));
|
||||
|
||||
total_gc_time += s->gc_time;
|
||||
}
|
||||
u64 total_gc_time =
|
||||
gc_stats.nursery_stats.gc_time +
|
||||
gc_stats.aging_stats.gc_time +
|
||||
gc_stats.full_stats.gc_time;
|
||||
|
||||
result.add(tag<bignum>(ulong_long_to_bignum(total_gc_time)));
|
||||
result.add(tag<bignum>(ulong_long_to_bignum(gc_stats.cards_scanned)));
|
||||
|
@ -186,7 +185,7 @@ void factor_vm::inline_gc(cell *gc_roots_base, cell gc_roots_size)
|
|||
for(cell i = 0; i < gc_roots_size; i++)
|
||||
gc_locals.push_back((cell)&gc_roots_base[i]);
|
||||
|
||||
garbage_collection(nursery_gen,false,true,0);
|
||||
garbage_collection(collect_nursery_op,true,0);
|
||||
|
||||
for(cell i = 0; i < gc_roots_size; i++)
|
||||
gc_locals.pop_back();
|
||||
|
@ -215,7 +214,7 @@ object *factor_vm::allot_object(header header, cell size)
|
|||
{
|
||||
/* If there is insufficient room, collect the nursery */
|
||||
if(nursery.here + size > nursery.end)
|
||||
garbage_collection(nursery_gen,false,true,0);
|
||||
garbage_collection(collect_nursery_op,true,0);
|
||||
|
||||
obj = nursery.allot(size);
|
||||
}
|
||||
|
@ -229,7 +228,7 @@ object *factor_vm::allot_object(header header, cell size)
|
|||
|
||||
/* If it still won't fit, grow the heap */
|
||||
if(data->tenured->here + size > data->tenured->end)
|
||||
garbage_collection(tenured_gen,true,true,size);
|
||||
garbage_collection(collect_growing_heap_op,true,size);
|
||||
|
||||
obj = data->tenured->allot(size);
|
||||
|
||||
|
|
51
vm/gc.hpp
51
vm/gc.hpp
|
@ -1,6 +1,14 @@
|
|||
namespace factor
|
||||
{
|
||||
|
||||
enum gc_op {
|
||||
collect_nursery_op,
|
||||
collect_aging_op,
|
||||
collect_to_tenured_op,
|
||||
collect_full_op,
|
||||
collect_growing_heap_op
|
||||
};
|
||||
|
||||
/* statistics */
|
||||
struct generation_statistics {
|
||||
cell collections;
|
||||
|
@ -11,7 +19,9 @@ struct generation_statistics {
|
|||
};
|
||||
|
||||
struct gc_statistics {
|
||||
generation_statistics generations[gen_count];
|
||||
generation_statistics nursery_stats;
|
||||
generation_statistics aging_stats;
|
||||
generation_statistics full_stats;
|
||||
u64 cards_scanned;
|
||||
u64 decks_scanned;
|
||||
u64 card_scan_time;
|
||||
|
@ -19,47 +29,12 @@ struct gc_statistics {
|
|||
};
|
||||
|
||||
struct gc_state {
|
||||
/* The data heap we're collecting */
|
||||
data_heap *data;
|
||||
|
||||
/* sometimes we grow the heap */
|
||||
bool growing_data_heap;
|
||||
|
||||
/* Which generation is being collected */
|
||||
cell collecting_gen;
|
||||
|
||||
/* If true, we are collecting aging space for the second time, so if it is still
|
||||
full, we go on to collect tenured */
|
||||
bool collecting_aging_again;
|
||||
|
||||
/* GC start time, for benchmarking */
|
||||
gc_op op;
|
||||
u64 start_time;
|
||||
|
||||
jmp_buf gc_unwind;
|
||||
|
||||
explicit gc_state(data_heap *data_, bool growing_data_heap_, cell collecting_gen_);
|
||||
explicit gc_state(gc_op op_);
|
||||
~gc_state();
|
||||
|
||||
inline bool collecting_nursery_p()
|
||||
{
|
||||
return collecting_gen == nursery_gen;
|
||||
}
|
||||
|
||||
inline bool collecting_aging_p()
|
||||
{
|
||||
return collecting_gen == aging_gen;
|
||||
}
|
||||
|
||||
inline bool collecting_tenured_p()
|
||||
{
|
||||
return collecting_gen == tenured_gen;
|
||||
}
|
||||
|
||||
inline bool collecting_accumulation_gen_p()
|
||||
{
|
||||
return ((collecting_aging_p() && !collecting_aging_again)
|
||||
|| collecting_tenured_p());
|
||||
}
|
||||
};
|
||||
|
||||
VM_C_API void inline_gc(cell *gc_roots_base, cell gc_roots_size, factor_vm *myvm);
|
||||
|
|
|
@ -4,8 +4,11 @@ namespace factor
|
|||
{
|
||||
|
||||
nursery_collector::nursery_collector(factor_vm *myvm_) :
|
||||
copying_collector<aging_space,nursery_policy>
|
||||
(myvm_,myvm_->data->aging,nursery_policy(myvm_)) {}
|
||||
copying_collector<aging_space,nursery_policy>(
|
||||
myvm_,
|
||||
&myvm_->gc_stats.nursery_stats,
|
||||
myvm_->data->aging,
|
||||
nursery_policy(myvm_)) {}
|
||||
|
||||
void factor_vm::collect_nursery()
|
||||
{
|
||||
|
|
|
@ -4,8 +4,11 @@ namespace factor
|
|||
{
|
||||
|
||||
to_tenured_collector::to_tenured_collector(factor_vm *myvm_) :
|
||||
copying_collector<tenured_space,to_tenured_policy>
|
||||
(myvm_,myvm_->data->tenured,to_tenured_policy(myvm_)) {}
|
||||
copying_collector<tenured_space,to_tenured_policy>(
|
||||
myvm_,
|
||||
&myvm_->gc_stats.aging_stats,
|
||||
myvm_->data->tenured,
|
||||
to_tenured_policy(myvm_)) {}
|
||||
|
||||
void factor_vm::collect_to_tenured()
|
||||
{
|
||||
|
|
11
vm/vm.hpp
11
vm/vm.hpp
|
@ -1,6 +1,8 @@
|
|||
namespace factor
|
||||
{
|
||||
|
||||
struct growable_array;
|
||||
|
||||
struct factor_vm
|
||||
{
|
||||
// First five fields accessed directly by assembler. See vm.factor
|
||||
|
@ -240,8 +242,8 @@ struct factor_vm
|
|||
void collect_full_impl(bool trace_contexts_p);
|
||||
void collect_growing_heap(cell requested_bytes, bool trace_contexts_p);
|
||||
void collect_full(bool trace_contexts_p);
|
||||
void record_gc_stats();
|
||||
void garbage_collection(cell gen, bool growing_data_heap, bool trace_contexts_p, cell requested_bytes);
|
||||
void record_gc_stats(generation_statistics *stats);
|
||||
void garbage_collection(gc_op op, bool trace_contexts_p, cell requested_bytes);
|
||||
void gc();
|
||||
void primitive_gc();
|
||||
void primitive_gc_stats();
|
||||
|
@ -249,6 +251,7 @@ struct factor_vm
|
|||
void primitive_become();
|
||||
void inline_gc(cell *gc_roots_base, cell gc_roots_size);
|
||||
object *allot_object(header header, cell size);
|
||||
void add_gc_stats(generation_statistics *stats, growable_array *result);
|
||||
void primitive_clear_gc_stats();
|
||||
|
||||
template<typename Type> Type *allot(cell size)
|
||||
|
@ -259,7 +262,7 @@ struct factor_vm
|
|||
inline void check_data_pointer(object *pointer)
|
||||
{
|
||||
#ifdef FACTOR_DEBUG
|
||||
if(!(current_gc && current_gc->growing_data_heap))
|
||||
if(!(current_gc && current_gc->op == collect_growing_heap_op))
|
||||
{
|
||||
assert((cell)pointer >= data->seg->start
|
||||
&& (cell)pointer < data->seg->end);
|
||||
|
@ -298,7 +301,7 @@ struct factor_vm
|
|||
void print_callstack();
|
||||
void dump_cell(cell x);
|
||||
void dump_memory(cell from, cell to);
|
||||
void dump_zone(cell gen, zone *z);
|
||||
void dump_zone(char *name, zone *z);
|
||||
void dump_generations();
|
||||
void dump_objects(cell type);
|
||||
void find_data_references_step(cell *scan);
|
||||
|
|
Loading…
Reference in New Issue