]> gitweb.factorcode.org Git - factor.git/commitdiff
Clean up some GC logic and fix a bug where large object allocation could grow the...
authorSlava Pestov <slava@factorcode.org>
Mon, 5 Sep 2011 23:21:09 +0000 (16:21 -0700)
committerSlava Pestov <slava@factorcode.org>
Mon, 5 Sep 2011 23:28:00 +0000 (16:28 -0700)
vm/aging_collector.cpp
vm/compaction.cpp
vm/data_heap.cpp
vm/data_heap.hpp
vm/full_collector.cpp
vm/gc.cpp

index c747592f42d2e4fae7beeb68818f6461e0a7f8cc..4bd1b81ec7bb277975bf7540c7cc8b421812e1ee 100644 (file)
@@ -14,10 +14,8 @@ void factor_vm::collect_aging()
        /* Promote objects referenced from tenured space to tenured space, copy
        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
-               tenured collection. We are collecting to tenured space, and
-               cards were unmarked, so we can't proceed with a to_tenured
-               collection. */
+               /* Change the op so that if we fail here, an assertion will be
+               raised. */
                current_gc->op = collect_to_tenured_op;
 
                to_tenured_collector collector(this);
index 343a61b8badfd2faa17f92af0b1c40e4565b5304..b7d9b611a326b9197e42aaa57cf2c3ec3bac6b41 100644 (file)
@@ -330,6 +330,14 @@ void factor_vm::collect_compact(bool trace_contexts_p)
 {
        collect_mark_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();
 }
 
index 3648ba7f4827c7acf00a75694048beab30384c66..08f3e929cad25f7b3bc96a52d21de225b4df6020 100755 (executable)
@@ -100,12 +100,12 @@ void data_heap::reset_generation(tenured_space *gen)
 
 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()
 {
-       return (tenured->free_space() <= nursery->size + aging->size);
+       return (tenured->free_space() <= high_water_mark());
 }
 
 void data_heap::mark_all_cards()
index cef43ef5fe9a03d5863e78ce6240759f2e4460aa..2be2cab93df1877fb438d6a1cf2a9f26f33bf6df 100755 (executable)
@@ -32,6 +32,9 @@ struct data_heap {
        bool high_fragmentation_p();
        bool low_memory_p();
        void mark_all_cards();
+       cell high_water_mark() {
+               return nursery->size + aging->size;
+       }
 };
 
 struct data_heap_room {
index 852c058bd255d2e0075c9384041a8a48a959be8b..71f04cf6ce1025a2b1dab2b2885f12115a5cacd7 100644 (file)
@@ -112,11 +112,14 @@ void factor_vm::collect_full(bool trace_contexts_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);
                collect_growing_heap(0,trace_contexts_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);
                collect_compact_impl(trace_contexts_p);
        }
index 1bb339a70ab7bd9c949db886d1685d3d789c2738..ba4753d4006846bda0aed7a03e4e936a20cab7eb 100755 (executable)
--- a/vm/gc.cpp
+++ b/vm/gc.cpp
@@ -116,19 +116,19 @@ void factor_vm::start_gc_again()
        switch(current_gc->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;
                break;
        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;
                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:
+               /* Nothing else should fail mid-collection due to insufficient
+               space in the target generation. */
                critical_error("Bad GC op",current_gc->op);
                break;
        }
@@ -148,10 +148,16 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p)
        assert(!gc_off);
        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);
 
-       /* Keep trying to GC higher and higher generations until we don't run out
-       of space */
+       /* Keep trying to GC higher and higher generations until we don't run
+       out of space in the target generation. */
        for(;;)
        {
                try
@@ -164,17 +170,23 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p)
                                collect_nursery();
                                break;
                        case collect_aging_op:
+                               /* We end up here if the above fails. */
                                collect_aging();
                                if(data->high_fragmentation_p())
                                {
+                                       /* Change GC op so that if we fail again,
+                                       we crash. */
                                        set_current_gc_op(collect_full_op);
                                        collect_full(trace_contexts_p);
                                }
                                break;
                        case collect_to_tenured_op:
+                               /* We end up here if the above fails. */
                                collect_to_tenured();
                                if(data->high_fragmentation_p())
                                {
+                                       /* Change GC op so that if we fail again,
+                                       we crash. */
                                        set_current_gc_op(collect_full_op);
                                        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 &)
                {
-                       /* We come back here if a generation is full */
+                       /* We come back here if the target generation is full. */
                        start_gc_again();
                        continue;
                }
@@ -207,6 +219,9 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p)
 
        delete current_gc;
        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
@@ -283,7 +298,7 @@ void factor_vm::primitive_compact_gc()
 object *factor_vm::allot_large_object(cell type, cell size)
 {
        /* 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();