various GC bugfixes

This commit is contained in:
Joel Dice 2007-06-22 14:55:11 -06:00
parent 09f6a34510
commit 3a6da507ec
3 changed files with 174 additions and 117 deletions

View File

@ -25,7 +25,7 @@ typedef void* object;
const unsigned BytesPerWord = sizeof(uintptr_t); const unsigned BytesPerWord = sizeof(uintptr_t);
const unsigned BitsPerWord = BytesPerWord * 8; const unsigned BitsPerWord = BytesPerWord * 8;
const unsigned LikelyPageSize = 4 * 1024; const unsigned LikelyPageSizeInBytes = 4 * 1024;
inline unsigned inline unsigned
max(unsigned a, unsigned b) max(unsigned a, unsigned b)

View File

@ -12,8 +12,8 @@ namespace {
// before being copied to gen2: // before being copied to gen2:
const unsigned TenureThreshold = 3; const unsigned TenureThreshold = 3;
const unsigned MinimumGen1Size = 64 * 1024; const unsigned MinimumGen1SizeInBytes = 64 * 1024;
const unsigned MinimumGen2Size = 128 * 1024; const unsigned MinimumGen2SizeInBytes = 128 * 1024;
const unsigned Top = ~static_cast<unsigned>(0); const unsigned Top = ~static_cast<unsigned>(0);
@ -260,10 +260,9 @@ class Segment {
Chain* next; Chain* next;
Chain* previous; Chain* previous;
Chain(Segment* segment, unsigned offset, unsigned capacity, Chain(Segment* segment, unsigned capacity, Chain* previous):
Chain* previous):
segment(segment), segment(segment),
offset(offset), offset(previous ? previous->offset + previous->position : 0),
position(0), position(0),
capacity(capacity), capacity(capacity),
next(0), next(0),
@ -272,6 +271,28 @@ class Segment {
assert(segment->context, sizeof(Chain) % BytesPerWord == 0); assert(segment->context, sizeof(Chain) % BytesPerWord == 0);
} }
static Chain* make(Segment* s, unsigned minimum, unsigned desired) {
assert(s->context, minimum > 0);
assert(s->context, desired >= minimum);
void* p = 0;
unsigned capacity = desired;
while (p == 0) {
p = system(s->context)->tryAllocate
(footprint(capacity, s->rear, s->map) * BytesPerWord);
if (p == 0) {
if (capacity > minimum) {
capacity = minimum + ((capacity - minimum) / 2);
} else {
abort(s->context);
}
}
}
return new (p) Chain(s, capacity, s->rear);
}
static void dispose(Chain* c) { static void dispose(Chain* c) {
if (c) { if (c) {
if (c->next) dispose(c->next); if (c->next) dispose(c->next);
@ -308,27 +329,22 @@ class Segment {
Chain* rear; Chain* rear;
Map* map; Map* map;
Segment(Context* context, unsigned capacity, Map* map = 0, Segment(Context* context, unsigned minimum, unsigned desired, Map* map = 0,
bool clearMap = true): bool clearMap = true):
context(context), context(context),
front(0), front(0),
rear(0), rear(0),
map(map) map(map)
{ {
front = rear = 0; if (desired) {
this->map = map; front = rear = Chain::make(this, minimum, desired);
if (capacity) {
front = rear = new
(system(context)->allocate
(Chain::footprint(capacity, 0, map) * BytesPerWord))
Chain(this, 0, capacity, 0);
if (map) { if (map) {
map->setSegment(this);
if (clearMap) { if (clearMap) {
map->clear(); memset(front->data() + front->capacity, 0,
map->footprint(front->capacity) * BytesPerWord);
} }
map->setSegment(this);
} }
} }
} }
@ -356,18 +372,6 @@ class Segment {
abort(context); abort(context);
} }
void copyTo(void* p) {
for (Chain* c = front; c; c = c->next) {
if (c->position) {
memcpy(static_cast<uintptr_t*>(p) + c->offset,
c->data(),
c->position * BytesPerWord);
} else {
return;
}
}
}
unsigned footprint() { unsigned footprint() {
unsigned n = 0; unsigned n = 0;
for (Chain* c = front; c; c = c->next) n += c->footprint(); for (Chain* c = front; c; c = c->next) n += c->footprint();
@ -463,31 +467,14 @@ class Segment {
assert(context, rear->position); assert(context, rear->position);
assert(context, rear->next == 0); assert(context, rear->next == 0);
if (Verbose) {
fprintf(stderr, "grow\n");
}
unsigned desired = (position() + minimum) * 2; unsigned desired = (position() + minimum) * 2;
void* p = 0; Chain* c = Chain::make(this, minimum, desired);
unsigned newCapacity = desired;
while (p == 0) {
p = system(context)->tryAllocate
(Chain::footprint(newCapacity, rear, map) * BytesPerWord);
if (p == 0) {
if (newCapacity > minimum) {
newCapacity = minimum + ((newCapacity - minimum) / 2);
} else {
abort(context);
}
}
}
Chain* c = new (p) Chain
(this, rear->offset + rear->position, newCapacity, rear);
if (map) { if (map) {
memset(c->data() + c->capacity, 0,
map->footprint(c->offset + c->capacity) * BytesPerWord);
map->update(c->data() + c->capacity, c->offset + c->capacity); map->update(c->data() + c->capacity, c->offset + c->capacity);
} }
@ -515,10 +502,10 @@ class Context {
Context(System* system): Context(System* system):
system(system), system(system),
client(0), client(0),
gen1(this, 0), gen1(this, 0, 0),
nextGen1(this, 0), nextGen1(this, 0, 0),
gen2(this, 0), gen2(this, 0, 0),
nextGen2(this, 0) nextGen2(this, 0, 0)
{ } { }
void dispose() { void dispose() {
@ -540,13 +527,34 @@ class Context {
Segment::Map ageMap; Segment::Map ageMap;
Segment::Map nextAgeMap; Segment::Map nextAgeMap;
Segment::Map pointerMap; Segment::Map pointerMap;
Segment::Map pageMap; Segment::Map pageMap;
Segment::Map heapMap; Segment::Map heapMap;
Segment::Map nextPointerMap;
Segment::Map nextPageMap;
Segment::Map nextHeapMap;
CollectionMode mode; CollectionMode mode;
}; };
const char*
segment(Context* c, void* p)
{
if (c->gen1.contains(p)) {
return "gen1";
} else if (c->nextGen1.contains(p)) {
return "nextGen1";
} else if (c->gen2.contains(p)) {
return "gen2";
} else if (c->nextGen2.contains(p)) {
return "nextGen2";
} else {
return "none";
}
}
inline System* inline System*
system(Context* c) system(Context* c)
{ {
@ -569,43 +577,54 @@ assert(Context* c, bool v)
void void
initGen1(Context* c) initGen1(Context* c)
{ {
new (&(c->ageMap)) Segment::Map(&(c->gen1), log(TenureThreshold)); unsigned minimum = MinimumGen1SizeInBytes / BytesPerWord;
new (&(c->gen1)) Segment unsigned desired = minimum;
(c, MinimumGen1Size / BytesPerWord, &(c->ageMap), false);
}
void new (&(c->ageMap)) Segment::Map(&(c->gen1), log(TenureThreshold));
initGen2(Context* c)
{ new (&(c->gen1)) Segment(c, minimum, desired, &(c->ageMap), false);
new (&(c->pointerMap)) Segment::Map(&(c->gen2));
new (&(c->pageMap)) Segment::Map
(&(c->gen2), 1, LikelyPageSize / BytesPerWord, &(c->pointerMap));
new (&(c->heapMap)) Segment::Map
(&(c->gen2), 1, c->pageMap.scale * 1024, &(c->pageMap));
new (&(c->gen2)) Segment(c, MinimumGen2Size / BytesPerWord, &(c->heapMap));
} }
void void
initNextGen1(Context* c) initNextGen1(Context* c)
{ {
unsigned size = max(MinimumGen1Size / BytesPerWord, unsigned minimum = MinimumGen1SizeInBytes / BytesPerWord;
nextPowerOfTwo(c->gen1.position())); unsigned desired = max(minimum, nextPowerOfTwo(c->gen1.position()));
new (&(c->nextAgeMap)) Segment::Map(&(c->nextGen1), log(TenureThreshold)); new (&(c->nextAgeMap)) Segment::Map(&(c->nextGen1), log(TenureThreshold));
new (&(c->nextGen1)) Segment(c, size, &(c->nextAgeMap), false);
new (&(c->nextGen1)) Segment(c, minimum, desired, &(c->nextAgeMap), false);
}
void
initGen2(Context* c)
{
unsigned minimum = MinimumGen2SizeInBytes / BytesPerWord;
unsigned desired = minimum;
new (&(c->pointerMap)) Segment::Map(&(c->gen2));
new (&(c->pageMap)) Segment::Map
(&(c->gen2), 1, LikelyPageSizeInBytes / BytesPerWord, &(c->pointerMap));
new (&(c->heapMap)) Segment::Map
(&(c->gen2), 1, c->pageMap.scale * 1024, &(c->pageMap));
new (&(c->gen2)) Segment(c, minimum, desired, &(c->heapMap));
} }
void void
initNextGen2(Context* c) initNextGen2(Context* c)
{ {
unsigned size = max(MinimumGen2Size / BytesPerWord, unsigned minimum = MinimumGen2SizeInBytes / BytesPerWord;
nextPowerOfTwo(c->gen2.position())); unsigned desired = max(minimum, nextPowerOfTwo(c->gen2.position()));
new (&(c->pointerMap)) Segment::Map(&(c->nextGen2));
new (&(c->pageMap)) Segment::Map new (&(c->nextPointerMap)) Segment::Map(&(c->nextGen2));
(&(c->nextGen2), 1, LikelyPageSize / BytesPerWord, &(c->pointerMap)); new (&(c->nextPageMap)) Segment::Map
new (&(c->heapMap)) Segment::Map (&(c->nextGen2), 1, LikelyPageSizeInBytes / BytesPerWord,
(&(c->nextGen2), 1, c->pageMap.scale * 1024, &(c->pageMap)); &(c->nextPointerMap));
new (&(c->nextGen2)) Segment(c, size, &(c->heapMap)); new (&(c->nextHeapMap)) Segment::Map
c->gen2.map = 0; (&(c->nextGen2), 1, c->pageMap.scale * 1024, &(c->nextPageMap));
new (&(c->nextGen2)) Segment(c, minimum, desired, &(c->nextHeapMap));
} }
inline bool inline bool
@ -644,9 +663,24 @@ bitset(Context* c, object o)
} }
inline object inline object
copyTo(Context*, Segment* s, object o, unsigned size) copyTo(Context* c, Segment* s, object o, unsigned size)
{ {
s->ensure(size); if (s->remaining() < size) {
if (Verbose) {
if (s == &(c->gen2)) {
fprintf(stderr, "grow gen2\n");
} else if (s == &(c->nextGen1)) {
fprintf(stderr, "grow nextGen1\n");
} else if (s == &(c->nextGen2)) {
fprintf(stderr, "grow nextGen2\n");
} else {
abort(c);
}
}
s->ensure(size);
}
return static_cast<object>(s->add(o, size)); return static_cast<object>(s->add(o, size));
} }
@ -707,7 +741,8 @@ copy(Context* c, object o)
object r = copy2(c, o); object r = copy2(c, o);
if (Debug) { if (Debug) {
fprintf(stderr, "copy %p to %p\n", o, r); fprintf(stderr, "copy %p (%s) to %p (%s)\n",
o, segment(c, o), r, segment(c, r));
} }
// leave a pointer to the copy in the original // leave a pointer to the copy in the original
@ -744,6 +779,8 @@ update2(Context* c, object* p, bool* needsVisit)
if (c->gen2.contains(*p)) { if (c->gen2.contains(*p)) {
return update3(c, p, needsVisit); return update3(c, p, needsVisit);
} else { } else {
assert(c, c->nextGen1.contains(*p) or c->nextGen2.contains(*p));
*needsVisit = false; *needsVisit = false;
return *p; return *p;
} }
@ -769,11 +806,21 @@ update(Context* c, object* p, bool* needsVisit)
if (r) { if (r) {
if (c->mode == MinorCollection) { if (c->mode == MinorCollection) {
if (c->gen2.contains(p) and not c->gen2.contains(r)) { if (c->gen2.contains(p) and not c->gen2.contains(r)) {
if (Debug) {
fprintf(stderr, "mark %p (%s) at %p (%s)\n",
r, segment(c, r), p, segment(c, p));
}
c->heapMap.set(p); c->heapMap.set(p);
} }
} else { } else {
if (c->nextGen2.contains(p) and not c->nextGen2.contains(r)) { if (c->nextGen2.contains(p) and not c->nextGen2.contains(r)) {
c->heapMap.set(p); if (Debug) {
fprintf(stderr, "mark %p (%s) at %p (%s)\n",
r, segment(c, r), p, segment(c, p));
}
c->nextHeapMap.set(p);
} }
} }
} }
@ -888,14 +935,16 @@ collect(Context* c, void** p)
object parent = 0; object parent = 0;
if (Debug) { if (Debug) {
fprintf(stderr, "update %p at %p\n", *p, p); fprintf(stderr, "update %p (%s) at %p (%s)\n",
*p, segment(c, *p), p, segment(c, p));
} }
bool needsVisit; bool needsVisit;
*p = update(c, p, &needsVisit); *p = update(c, p, &needsVisit);
if (Debug) { if (Debug) {
fprintf(stderr, " result: %p (visit? %d)\n", *p, needsVisit); fprintf(stderr, " result: %p (%s) (visit? %d)\n",
*p, segment(c, *p), needsVisit);
} }
if (not needsVisit) return; if (not needsVisit) return;
@ -918,11 +967,13 @@ collect(Context* c, void** p)
virtual bool visit(unsigned offset) { virtual bool visit(unsigned offset) {
if (Debug) { if (Debug) {
fprintf(stderr, " update %p at %p - offset %d from %p\n", fprintf(stderr, " update %p (%s) at %p - offset %d from %p (%s)\n",
cast<object>(copy, offset * BytesPerWord), cast<object>(copy, offset * BytesPerWord),
segment(c, cast<object>(copy, offset * BytesPerWord)),
&cast<object>(copy, offset * BytesPerWord), &cast<object>(copy, offset * BytesPerWord),
offset, offset,
copy); copy,
segment(c, copy));
} }
bool needsVisit; bool needsVisit;
@ -930,8 +981,8 @@ collect(Context* c, void** p)
(c, &cast<object>(copy, offset * BytesPerWord), &needsVisit); (c, &cast<object>(copy, offset * BytesPerWord), &needsVisit);
if (Debug) { if (Debug) {
fprintf(stderr, " result: %p (visit? %d)\n", fprintf(stderr, " result: %p (%s) (visit? %d)\n",
childCopy, needsVisit); childCopy, segment(c, childCopy), needsVisit);
} }
++ total; ++ total;
@ -980,7 +1031,7 @@ collect(Context* c, void** p)
} walker(c, copy, bitset(c, original)); } walker(c, copy, bitset(c, original));
if (Debug) { if (Debug) {
fprintf(stderr, "walk %p\n", copy); fprintf(stderr, "walk %p (%s)\n", copy, segment(c, copy));
} }
c->client->walk(copy, &walker); c->client->walk(copy, &walker);
@ -1052,11 +1103,13 @@ collect(Context* c, void** p)
} }
if (Debug) { if (Debug) {
fprintf(stderr, " next is %p at %p - offset %d from %p\n", fprintf(stderr, " next is %p (%s) at %p - offset %d from %p (%s)\n",
cast<object>(copy, walker.next * BytesPerWord), cast<object>(copy, walker.next * BytesPerWord),
segment(c, cast<object>(copy, walker.next * BytesPerWord)),
&cast<object>(copy, walker.next * BytesPerWord), &cast<object>(copy, walker.next * BytesPerWord),
walker.next, walker.next,
copy); copy,
segment(c, copy));
} }
original = cast<object>(copy, walker.next * BytesPerWord); original = cast<object>(copy, walker.next * BytesPerWord);
@ -1082,9 +1135,7 @@ collect(Context* c, Segment::Map* map, unsigned start, unsigned end,
map->clearOnly(s); map->clearOnly(s);
bool childDirty = false; bool childDirty = false;
collect(c, map->child, s, e, &childDirty, true); collect(c, map->child, s, e, &childDirty, true);
if (c->mode == OverflowCollection) { if (c->mode != OverflowCollection and childDirty) {
return;
} else if (childDirty) {
map->setOnly(s); map->setOnly(s);
*dirty = true; *dirty = true;
} }
@ -1099,11 +1150,7 @@ collect(Context* c, Segment::Map* map, unsigned start, unsigned end,
} else { } else {
collect(c, p); collect(c, p);
if (c->mode == OverflowCollection) { if (c->mode != OverflowCollection and not c->gen2.contains(*p)) {
return;
} else if (c->gen2.contains(*p)) {
// done
} else {
map->setOnly(p); map->setOnly(p);
*dirty = true; *dirty = true;
} }
@ -1290,6 +1337,11 @@ makeHeap(System* system)
} }
virtual void mark(void** p) { virtual void mark(void** p) {
if (Debug) {
fprintf(stderr, "mark %p (%s) at %p (%s)\n",
*p, segment(&c, *p), p, segment(&c, p));
}
c.heapMap.set(p); c.heapMap.set(p);
} }
@ -1301,7 +1353,9 @@ makeHeap(System* system)
virtual void* follow(void* p) { virtual void* follow(void* p) {
if (wasCollected(&c, p)) { if (wasCollected(&c, p)) {
if (Debug) { if (Debug) {
fprintf(stderr, "follow: %p: %p\n", p, ::follow(&c, p)); fprintf(stderr, "follow %p (%s) to %p (%s)\n",
p, segment(&c, p),
::follow(&c, p), segment(&c, ::follow(&c, p)));
} }
return ::follow(&c, p); return ::follow(&c, p);

View File

@ -91,8 +91,8 @@ class Thread {
Protector* next; Protector* next;
}; };
static const unsigned HeapSize = 64 * 1024; static const unsigned HeapSizeInBytes = 64 * 1024;
static const unsigned StackSize = 64 * 1024; static const unsigned StackSizeInBytes = 64 * 1024;
Thread(Machine* m); Thread(Machine* m);
@ -107,8 +107,8 @@ class Thread {
unsigned ip; unsigned ip;
unsigned sp; unsigned sp;
unsigned heapIndex; unsigned heapIndex;
object stack[StackSize]; object stack[StackSizeInBytes / BytesPerWord];
object heap[HeapSize]; object heap[HeapSizeInBytes / BytesPerWord];
Protector* protector; Protector* protector;
}; };
@ -449,9 +449,9 @@ enter(Thread* t, Thread::State s)
} }
void void
maybeYieldAndMaybeCollect(Thread* t, unsigned size) maybeYieldAndMaybeCollect(Thread* t, unsigned sizeInBytes)
{ {
if (size > Thread::HeapSize) { if (sizeInBytes > Thread::HeapSizeInBytes) {
// large object support not yet implemented. // large object support not yet implemented.
abort(t); abort(t);
} }
@ -465,7 +465,9 @@ maybeYieldAndMaybeCollect(Thread* t, unsigned size)
enter(t, Thread::ActiveState); enter(t, Thread::ActiveState);
} }
if (t->heapIndex + size >= Thread::HeapSize) { if (t->heapIndex + divide(sizeInBytes, BytesPerWord)
>= (Thread::HeapSizeInBytes / BytesPerWord))
{
enter(t, Thread::ExclusiveState); enter(t, Thread::ExclusiveState);
collect(t->vm, Heap::MinorCollection); collect(t->vm, Heap::MinorCollection);
enter(t, Thread::ActiveState); enter(t, Thread::ActiveState);
@ -473,16 +475,17 @@ maybeYieldAndMaybeCollect(Thread* t, unsigned size)
} }
inline object inline object
allocate(Thread* t, unsigned size) allocate(Thread* t, unsigned sizeInBytes)
{ {
if (UNLIKELY(t->heapIndex + size >= Thread::HeapSize if (UNLIKELY(t->heapIndex + divide(sizeInBytes, BytesPerWord)
>= (Thread::HeapSizeInBytes / BytesPerWord)
or t->vm->exclusive)) or t->vm->exclusive))
{ {
maybeYieldAndMaybeCollect(t, size); maybeYieldAndMaybeCollect(t, sizeInBytes);
} }
object o = t->heap + t->heapIndex; object o = t->heap + t->heapIndex;
t->heapIndex += size; t->heapIndex += divide(sizeInBytes, BytesPerWord);
return o; return o;
} }
@ -518,11 +521,11 @@ inline object
make(Thread* t, object class_) make(Thread* t, object class_)
{ {
PROTECT(t, class_); PROTECT(t, class_);
unsigned size = classFixedSize(t, class_); unsigned sizeInBytes = classFixedSize(t, class_);
object instance = allocate(t, size); object instance = allocate(t, sizeInBytes);
*static_cast<object*>(instance) = class_; *static_cast<object*>(instance) = class_;
memset(static_cast<object*>(instance) + sizeof(object), 0, memset(static_cast<object*>(instance) + sizeof(object), 0,
size - sizeof(object)); sizeInBytes - sizeof(object));
return instance; return instance;
} }
@ -1241,7 +1244,7 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool)
unsigned memberOffset = BytesPerWord; unsigned memberOffset = BytesPerWord;
if (classSuper(t, class_)) { if (classSuper(t, class_)) {
memberOffset = classFixedSize(t, classSuper(t, class_)) * BytesPerWord; memberOffset = classFixedSize(t, classSuper(t, class_));
} }
unsigned count = s.read2(); unsigned count = s.read2();
@ -3033,7 +3036,7 @@ run(Thread* t)
invoke: invoke:
if (UNLIKELY(codeMaxStack(t, methodCode(t, code)) + sp - parameterCount if (UNLIKELY(codeMaxStack(t, methodCode(t, code)) + sp - parameterCount
> Thread::StackSize)) > (Thread::StackSizeInBytes / BytesPerWord)))
{ {
exception = makeStackOverflowError(t); exception = makeStackOverflowError(t);
goto throw_; goto throw_;