static void markFiber(WrenVM* vm, ObjFiber* fiber) { if (setMarkedFlag(&fiber->obj)) return; // Stack functions. for (int i = 0; i < fiber->numFrames; i++) { wrenMarkObj(vm, fiber->frames[i].fn); } // Stack variables. for (Value* slot = fiber->stack; slot < fiber->stackTop; slot++) { wrenMarkValue(vm, *slot); } // Open upvalues. Upvalue* upvalue = fiber->openUpvalues; while (upvalue != NULL) { markUpvalue(vm, upvalue); upvalue = upvalue->next; } // The caller. if (fiber->caller != NULL) markFiber(vm, fiber->caller); if (fiber->error != NULL) markString(vm, fiber->error); // Keep track of how much memory is still in use. vm->bytesAllocated += sizeof(ObjFiber); // TODO: Count size of error message buffer. }
static void markUpvalue(WrenVM* vm, Upvalue* upvalue) { // This can happen if a GC is triggered in the middle of initializing the // closure. if (upvalue == NULL) return; if (setMarkedFlag(&upvalue->obj)) return; // Mark the closed-over object (in case it is closed). wrenMarkValue(vm, upvalue->closed); // Keep track of how much memory is still in use. vm->bytesAllocated += sizeof(Upvalue); }
static void markInstance(WrenVM* vm, ObjInstance* instance) { if (setMarkedFlag(&instance->obj)) return; markClass(vm, instance->obj.classObj); // Mark the fields. for (int i = 0; i < instance->obj.classObj->numFields; i++) { wrenMarkValue(vm, instance->fields[i]); } // Keep track of how much memory is still in use. vm->bytesAllocated += sizeof(ObjInstance); vm->bytesAllocated += sizeof(Value) * instance->obj.classObj->numFields; }
static void markList(WrenVM* vm, ObjList* list) { if (setMarkedFlag(&list->obj)) return; // Mark the elements. Value* elements = list->elements; for (int i = 0; i < list->count; i++) { wrenMarkValue(vm, elements[i]); } // Keep track of how much memory is still in use. vm->bytesAllocated += sizeof(ObjList); if (list->elements != NULL) { vm->bytesAllocated += sizeof(Value) * list->capacity; } }
static void markFn(WrenVM* vm, ObjFn* fn) { if (setMarkedFlag(&fn->obj)) return; // Mark the constants. for (int i = 0; i < fn->numConstants; i++) { wrenMarkValue(vm, fn->constants[i]); } wrenMarkObj(vm, (Obj*)fn->debug->sourcePath); // Keep track of how much memory is still in use. vm->bytesAllocated += sizeof(ObjFn); vm->bytesAllocated += sizeof(uint8_t) * fn->bytecodeLength; vm->bytesAllocated += sizeof(Value) * fn->numConstants; // The debug line number buffer. vm->bytesAllocated += sizeof(int) * fn->bytecodeLength; // TODO: What about the function name? }
static void collectGarbage(WrenVM* vm) { #if WREN_DEBUG_TRACE_MEMORY || WREN_DEBUG_TRACE_GC printf("-- gc --\n"); size_t before = vm->bytesAllocated; double startTime = (double)clock() / CLOCKS_PER_SEC; #endif // Mark all reachable objects. // Reset this. As we mark objects, their size will be counted again so that // we can track how much memory is in use without needing to know the size // of each *freed* object. // // This is important because when freeing an unmarked object, we don't always // know how much memory it is using. For example, when freeing an instance, // we need to know its class to know how big it is, but it's class may have // already been freed. vm->bytesAllocated = 0; // Global variables. for (int i = 0; i < vm->globals.count; i++) { wrenMarkValue(vm, vm->globals.data[i]); } // Pinned objects. WrenPinnedObj* pinned = vm->pinned; while (pinned != NULL) { wrenMarkObj(vm, pinned->obj); pinned = pinned->previous; } // The current fiber. if (vm->fiber != NULL) wrenMarkObj(vm, (Obj*)vm->fiber); // Any object the compiler is using (if there is one). if (vm->compiler != NULL) wrenMarkCompiler(vm, vm->compiler); // Collect any unmarked objects. Obj** obj = &vm->first; while (*obj != NULL) { if (!((*obj)->flags & FLAG_MARKED)) { // This object wasn't reached, so remove it from the list and free it. Obj* unreached = *obj; *obj = unreached->next; wrenFreeObj(vm, unreached); } else { // This object was reached, so unmark it (for the next GC) and move on to // the next. (*obj)->flags &= ~FLAG_MARKED; obj = &(*obj)->next; } } vm->nextGC = vm->bytesAllocated * vm->heapScalePercent / 100; if (vm->nextGC < vm->minNextGC) vm->nextGC = vm->minNextGC; #if WREN_DEBUG_TRACE_MEMORY || WREN_DEBUG_TRACE_GC double elapsed = ((double)clock() / CLOCKS_PER_SEC) - startTime; printf("GC %ld before, %ld after (%ld collected), next at %ld. Took %.3fs.\n", before, vm->bytesAllocated, before - vm->bytesAllocated, vm->nextGC, elapsed); #endif }