Permalink
Browse files

Use separate marks for tracking mark state

This allows for using this separate list for concurrent marking. The
marking phase updates these marks, while allocations can still happen
without overwriting existing data.

After the GC phase is complete we copy over the marks during the final
stop the world phase so that for new allocations the new list is used.

This also adds a helper method that can be used to update the mark stack
to make sure it updates any values and removes invalid ones. This can be
used if we have to update the mark stack during concurrent GC.

Introduces an additional argument for process_mark_stack so we can
partially process the mark stack. This can be used so during concurrent
GC we can yield and for example a young collection can happen.
  • Loading branch information...
dbussink committed May 24, 2013
1 parent 724881f commit 55f29bfd4c5d9f731ea1ca1869f6b1188c6a438d
Showing with 118 additions and 27 deletions.
  1. +4 −1 vm/gc/immix.cpp
  2. +19 −6 vm/gc/immix.hpp
  3. +40 −3 vm/test/test_gc_immix.hpp
  4. +55 −17 vm/util/immix.hpp
View
@@ -135,7 +135,7 @@ namespace rubinius {
* Performs a garbage collection of the immix space.
*/
void ImmixGC::collect(GCData& data) {
gc_.clear_lines();
gc_.clear_marks();
int via_handles_ = 0;
int via_roots = 0;
@@ -215,6 +215,9 @@ namespace rubinius {
unsigned int mark = object_memory_->mark();
object_memory_->unremember_objects(mark);
// Copy marks for use in new allocations
gc_.copy_marks();
// Sweep up the garbage
gc_.sweep_blocks();
View
@@ -73,7 +73,22 @@ namespace rubinius {
memory::Address copy(memory::Address original, immix::Allocator& alloc);
void walk_pointers(memory::Address addr, immix::Marker<ObjectDescriber>& mark) {
gc_->scan_object(addr.as<Object>());
Object* obj = addr.as<Object>();
if(obj) gc_->scan_object(obj);
}
memory::Address update_pointer(memory::Address addr) {
Object* obj = addr.as<Object>();
if(!obj) return memory::Address::null();
if(obj->young_object_p()) {
if(obj->forwarded_p()) return obj->forward();
return memory::Address::null();
} else {
// we must remember this because it might
// contain references to young gen objects
object_memory_->remember_object(obj);
}
return addr;
}
int size(memory::Address addr);
@@ -84,19 +99,17 @@ namespace rubinius {
* @returns true if the object is not already marked, and in the Immix
* space; otherwise false.
*/
bool mark_address(memory::Address addr, immix::MarkStack& ms) {
inline bool mark_address(memory::Address addr, immix::MarkStack& ms, bool push = true) {
Object* obj = addr.as<Object>();
if(obj->marked_p(object_memory_->mark())) return false;
obj->mark(object_memory_, object_memory_->mark());
gc_->inc_marked_objects();
ms.push_back(addr);
if(obj->in_immix_p()) return true;
if(push) ms.push_back(addr);
// If this is a young object, let the GC know not to try and mark
// the block it's in.
return false;
return obj->in_immix_p();
}
};
View
@@ -41,12 +41,12 @@ class SimpleObjectDescriber {
return copy;
}
bool mark_address(memory::Address addr, immix::MarkStack& ms) {
bool mark_address(memory::Address addr, immix::MarkStack& ms, bool push = true) {
SimpleObject* obj = addr.as<SimpleObject>();
if(obj->marked) return false;
obj->marked = true;
ms.push_back(obj);
if(push) ms.push_back(obj);
return true;
}
@@ -97,6 +97,8 @@ class TestImmixGC : public CxxTest::TestSuite {
immix::Block& block = gc->get_block();
TS_ASSERT(block.is_line_free(1));
block.mark_line(1);
TS_ASSERT(block.is_line_free(1));
block.copy_marks();
TS_ASSERT(!block.is_line_free(1));
}
@@ -123,6 +125,7 @@ class TestImmixGC : public CxxTest::TestSuite {
memory::Address top = block.first_address();
block.mark_line(2);
block.copy_marks();
immix::SingleBlockAllocator alloc(block);
alloc.allocate(96);
@@ -170,6 +173,7 @@ class TestImmixGC : public CxxTest::TestSuite {
block.mark_line(4);
block.mark_line(5);
block.mark_line(7);
block.copy_marks();
immix::SingleBlockAllocator alloc(block);
memory::Address addr = alloc.allocate(24);
@@ -193,6 +197,7 @@ class TestImmixGC : public CxxTest::TestSuite {
}
block.free_line(1);
block.copy_marks();
immix::SingleBlockAllocator alloc(block);
memory::Address small = alloc.allocate(24);
TS_ASSERT(!small.is_null());
@@ -281,6 +286,7 @@ class TestImmixGC : public CxxTest::TestSuite {
TS_ASSERT(block.is_line_free(1));
gc->mark_address(addr, alloc);
block.copy_marks();
TS_ASSERT(!block.is_line_free(1));
}
@@ -293,6 +299,7 @@ class TestImmixGC : public CxxTest::TestSuite {
TS_ASSERT(block.is_line_free(1));
gc->mark_address(addr, alloc);
block.copy_marks();
TS_ASSERT(block.is_line_free(1));
}
@@ -364,6 +371,7 @@ class TestImmixGC : public CxxTest::TestSuite {
memory::Address addr = alloc.allocate(sizeof(SimpleObject));
gc->mark_address(addr, alloc);
block.copy_marks();
TS_ASSERT(!block.is_line_free(0));
TS_ASSERT(!block.is_line_free(1));
@@ -372,13 +380,14 @@ class TestImmixGC : public CxxTest::TestSuite {
addr2.as<SimpleObject>()->size = big_size;
gc->mark_address(addr2, alloc);
block.copy_marks();
TS_ASSERT(!block.is_line_free(1));
TS_ASSERT(!block.is_line_free(2));
TS_ASSERT(!block.is_line_free(3));
TS_ASSERT(!block.is_line_free(4));
}
void test_process_mark_stack() {
void test_process_mark_stack_enabled() {
immix::Block& block = gc->get_block();
immix::SingleBlockAllocator alloc(block);
memory::Address addr = alloc.allocate(sizeof(SimpleObject));
@@ -404,6 +413,32 @@ class TestImmixGC : public CxxTest::TestSuite {
TS_ASSERT_EQUALS(sub->body_checked, true);
}
void test_process_mark_stack_disabled() {
immix::Block& block = gc->get_block();
immix::SingleBlockAllocator alloc(block);
memory::Address addr = alloc.allocate(sizeof(SimpleObject));
memory::Address addr2 = alloc.allocate(sizeof(SimpleObject));
SimpleObject* obj = addr.as<SimpleObject>();
SimpleObject* sub = addr2.as<SimpleObject>();
obj->marked = false;
obj->sub = sub;
obj->body_checked = false;
sub->marked = false;
sub->sub = 0;
sub->body_checked = false;
gc->mark_address(addr, alloc, false);
TS_ASSERT_EQUALS(obj->marked, true);
gc->process_mark_stack(alloc);
TS_ASSERT_EQUALS(obj->body_checked, false);
TS_ASSERT_EQUALS(sub->marked, false);
TS_ASSERT_EQUALS(sub->body_checked, false);
}
void test_BlockAllocator_reset_updates_block_stats() {
immix::Block& block = gc->get_block();
@@ -471,6 +506,7 @@ class TestImmixGC : public CxxTest::TestSuite {
}
block.free_line(1);
block.copy_marks();
ea.resync_position();
@@ -519,6 +555,7 @@ class TestImmixGC : public CxxTest::TestSuite {
void test_HoleFinder_find_hole_with_hole_in_middle() {
immix::Block& block = gc->get_block();
block.mark_line(11);
block.copy_marks();
immix::HoleFinder alloc;
View
@@ -121,6 +121,7 @@ namespace immix {
/// Map of in-use lines in the Block
LineEntry lines_[cLineTableSize];
LineEntry marks_[cLineTableSize];
public:
Block()
@@ -131,20 +132,27 @@ namespace immix {
, objects_(0)
, object_bytes_(0)
{
clear_lines();
memset(lines_, 0, sizeof(lines_));
memset(marks_, 0, sizeof(marks_));
marks_[0] = 1;
lines_[0] = 1;
}
/**
* Clears all lines in the Block, making the memory managed by this Block
* available for allocation.
*/
void clear_lines() {
void clear_marks() {
objects_ = 0;
object_bytes_ = 0;
memset(lines_, 0, sizeof(lines_));
memset(marks_, 0, sizeof(marks_));
// Always exclude the first line, it's got metadata in the form of a
// pointer back to this Block object managing that memory.
lines_[0] = 1;
marks_[0] = 1;
}
void copy_marks() {
memcpy(lines_, marks_, sizeof(lines_));
}
/**
@@ -241,14 +249,14 @@ namespace immix {
* Marks a line of memory as in use.
*/
void mark_line(int line) {
lines_[line] = 1;
marks_[line] = 1;
}
/**
* Marks a line of memory as free.
*/
void free_line(int line) {
lines_[line] = 0;
marks_[line] = 0;
}
/**
@@ -338,7 +346,7 @@ namespace immix {
lines_used_ = 0;
bool in_hole = false;
for(int i = 0; i < cLineTableSize; i++) {
if(lines_[i] == 0) {
if(marks_[i] == 0) {
if(!in_hole) holes_++;
in_hole = true;
} else {
@@ -1042,7 +1050,7 @@ namespace immix {
* for evacuation). Since this method does not actually know how to do the
* marking of an object, it calls back to ObjectDescriber to handle this.
*/
Address mark_address(Address addr, Allocator& alloc) {
Address mark_address(Address addr, Allocator& alloc, bool push = true) {
Address fwd = desc.forwarding_pointer(addr);
if(!fwd.is_null()) {
@@ -1051,7 +1059,7 @@ namespace immix {
// Returns false if addr is already marked, if so, we don't
// do the block marking logic again.
if(!desc.mark_address(addr, mark_stack_)) {
if(!desc.mark_address(addr, mark_stack_, push)) {
return addr;
}
@@ -1075,7 +1083,7 @@ namespace immix {
/**
* Calls the Describer to scan from each of the Addresses in the mark stack.
*/
bool process_mark_stack(Allocator& alloc) {
bool process_mark_stack(Allocator& alloc, int count = 0) {
Marker<Describer> mark(this, alloc);
if(mark_stack_.empty()) return false;
@@ -1085,11 +1093,30 @@ namespace immix {
Address addr = mark_stack_.back();
mark_stack_.pop_back();
desc.walk_pointers(addr, mark);
count--;
if(count == 0) return true;
}
return true;
}
/**
* Calls the describer to update addresses that might have been
* forwarded by another GC.
*/
size_t update_mark_stack(Allocator& alloc) {
Marker<Describer> mark(this, alloc);
for(MarkStack::iterator i = mark_stack_.begin(); i != mark_stack_.end(); ++i) {
Address addr = desc.update_pointer(*i);
if(!addr.is_null()) {
addr = mark_address(addr, alloc, false);
}
*i = addr;
}
return mark_stack_.size();
}
/**
* Notify the garbage collector that we have added a new Chunk.
*/
@@ -1106,17 +1133,28 @@ namespace immix {
}
/**
* Called at the start of a collection; invalidates all blocks and lines,
* making all the memory potentially available. Following a call to this
* method, the garbage collector will trace from all root objects, and
* live objects found in managed Blocks will cause lines to be re-marked
* as in use.
* Called at the start of a collection; clears marks before a new cycle.
* Following a call to this method, the garbage collector will trace
* from all root objects, and live objects found in managed Blocks will
* cause lines to be marked as in use.
*/
void clear_marks() {
AllBlockIterator iter(block_allocator_.chunks());
while(Block* block = iter.next()) {
block->clear_marks();
}
}
/**
* Called after a collection. Copies the marks to the current lines
* allocations will be able to use the updated lines list for finding holes
*/
void clear_lines() {
void copy_marks() {
AllBlockIterator iter(block_allocator_.chunks());
while(Block* block = iter.next()) {
block->clear_lines();
block->copy_marks();
}
}

0 comments on commit 55f29bf

Please sign in to comment.