Cut and paste the skeleton of the GC algorithm that will help you to answer the next questions.
private static void dumpHeap(String message, int[] heap, int hp, Dictionary dict) {
System.err.println(message);
for (var i = 0; i < hp; i++) {
var value = heap[i];
try {
System.err.println(i + ": " + value + " " + decodeAnyValue(value, dict, heap));
} catch (IndexOutOfBoundsException | ClassCastException e) {
System.err.println(i + ": " + value + " (can't decode)");
}
}
System.err.println();
}
private static void mark(int[] heap, int obj, Dictionary dict) {
// if already marked, return
// TODO
// mark the object at the GC_OFFSET
// TODO
// call recursively to mark all other objects following the fields
scanAnObject(heap, obj, dict, (addr, ref) -> mark(heap, ref, dict));
}
private static int gc(int requestedMemory, int[] stack, int sp, int bp, JSObject function, int[] heap, int hpEnd, Dictionary dict) {
System.err.println("--------- start a gc " + hpEnd);
// 1) scan the stack and recursively mark all reachable objects in the heap
scanStack(stack, sp, bp, function, dict, (addr, ref) -> mark(heap, ref, dict));
// 2) scan the heap to find the new addresses of all live objects
// and store them in the corresponding object gc slot
var virtual = new Object() { int hp; }; // 'virtual' hp, address of the object after the GC
scanHeap(heap, hpEnd, dict, (ref, __, clazz) -> {
// if its a live object
//TODO
{
// store the new address at gc offset
// TODO
// move virtual cursor
virtual.hp += OBJECT_HEADER_SIZE + clazz.length();
}
});
// DEBUG
System.err.println("new virtual hp at " + virtual.hp);
// 3) do we have enough free memory ?
if (virtual.hp > heap.length - requestedMemory) {
// we are morons
throw new Failure("out of memory error");
}
// 4) scan the heap to rewrite all field references to point to their new addresses
scanHeap(heap, hpEnd, dict, (ref, __, clazz) -> {
// if it's a live object (WARNING, it's not a GC_MARK anymore)
// TODO
{
// rewrite all reference fields
// TODO
}
});
// 5) scan the stack to rewrite the stack reference to point to the new addresses
scanStack(stack, sp, bp, function, dict, (addr, ref) -> {
// TODO
});
// 6) scan the heap and move the objects
scanHeap(heap, hpEnd, dict, (ref, tagClass, clazz) -> {
// if it's a live object
// TODO
{
// get new address
// TODO
// copy fields (in reverse order because old object and new object can overlap)
for (var i = clazz.length(); --i >= 0;) {
// TODO
}
// recreate the header
// TODO
}
});
return virtual.hp;
}
interface HeapClosure {
void accept(int ref, int tagClass, JSObject clazz);
}
private static void scanHeap(int[] heap, int hpEnd, Dictionary dict, HeapClosure heapClosure) {
// scan the heap and call the heapClosure on each objects of the heap
// TODO
}
interface StackClosure {
void accept(int addr, int ref);
}
private static void scanStack(int[] stack, int sp, int bp, JSObject function, Dictionary dict, StackClosure stackClosure) {
for (;;) {
// System.err.println("function " + function);
var code = (Code) function.lookup("__code__");
var activation = bp + code.getSlotVarCount();
// scan the stack and call the stack closure on all references
// System.err.println("scan stack");
// TODO
// scan the local variables and the stack closure on all references
// System.err.println("scan locals");
// TODO
// find previous function from activation frame
var previousFunction = stack[activation + FUN_OFFSET];
if (previousFunction == 0) {
// end of the stack
return;
}
// update sp, bp and function
sp = bp;
bp = stack[activation + BP_OFFSET];
function = (JSObject) decodeDictObject(previousFunction, dict);
}
}
private static void scanAnObject(int[] heap, int ref, Dictionary dict, StackClosure referenceClosure) {
// 1) get class descriptor from the heap
// TODO
// 2) Decoding the class object
// TODO
// DEBUG
//System.err.println("found class " + clazz + " at " + ref);
// 4) decode fields
for (var i = 0; i < clazz.length(); i++) {
// compute field address
// TODO
// get the field value
// TODO
// if is it a reference, call the reference closure
// TODO
}
}
private static final int GC_OFFSET = 1;
private static final int GC_MARK = -1;
private static final int GC_EMPTY = -2;
What is the purpose of the two functional interfaces ?