Bladeren bron

garbage collection

runningwater 2 jaren geleden
bovenliggende
commit
354058ecea
8 gewijzigde bestanden met toevoegingen van 126 en 5 verwijderingen
  1. 3 0
      chunk.c
  2. 1 1
      common.h
  3. 93 1
      memory.c
  4. 3 0
      object.c
  5. 8 0
      table.c
  6. 1 1
      table.h
  7. 11 2
      vm.c
  8. 6 0
      vm.h

+ 3 - 0
chunk.c

@@ -3,6 +3,7 @@
 //
 #include "chunk.h"
 #include "memory.h"
+#include "vm.h"
 
 void initChunk(Chunk *chunk) {
   chunk->count = 0;
@@ -32,6 +33,8 @@ void writeChunk(Chunk *chunk, uint8_t byte, int line) {
   chunk->count++;
 }
 int addConstant(Chunk *chunk, Value value) {
+  push(value);
   writeValueArray(&chunk->constants, value);
+  pop();
   return chunk->constants.count - 1;
 }

+ 1 - 1
common.h

@@ -13,7 +13,7 @@
 #include <string.h>
 
 #define DEBUG_PRINT_CODE
-#define DEBUG_TRACE_EXECUTION
+//#define DEBUG_TRACE_EXECUTION
 //#define DEBUG_STACK_INFO
 
 #define DEBUG_STRESS_GC

+ 93 - 1
memory.c

@@ -6,16 +6,25 @@
 #include <stdlib.h>
 #endif
 
+#define GC_HEAP_GROW_FACTOR 2
+
+static void markArray(ValueArray *);
+
 //    oldSize	 newSize	               Operation
 //    0	        Non‑zero	            Allocate new block.
 //    Non‑zero	0	                    Free allocation.
 //    Non‑zero	Smaller than oldSize	Shrink existing allocation.
 //    Non‑zero	Larger than oldSize	    Grow existing allocation.
 void *reallocate(void *pointer, size_t oldSize, size_t newSize) {
+  vm.bytesAllocated += newSize - oldSize;
   if (newSize > oldSize) {
 #ifdef DEBUG_STRESS_GC
     collectGarbage();
 #endif
+
+    if (vm.bytesAllocated > vm.nextGC) {
+      collectGarbage();
+    }
   }
 
   if (newSize == 0) {
@@ -76,6 +85,8 @@ void freeObjects() {
     freeObject(object);
     object = next;
   }
+
+  free(vm.grayStack);
 }
 static void markRoots() {
   for (Value *slot = vm.stack; slot < vm.stackTop; slot++) {
@@ -86,29 +97,101 @@ static void markRoots() {
     markObject((Obj *) vm.frames[i].closure);
   }
 
-  for (ObjUpvalue *upvalue = vm.openUpvalues; upvalue != NULL; upvalue = upvalue->next) {
+  for (ObjUpvalue *upvalue = vm.openUpvalues;
+       upvalue != NULL; upvalue = upvalue->next) {
     markObject((Obj *) upvalue);
   }
 
   markTable(&vm.globals);
   markCompilerRoots();
 }
+void blackenObject(Obj *object) {
+#ifdef DEBUG_LOG_GC
+  printf("%p blacken ", (void *) object);
+  printValue(OBJ_VAL(object));
+  printf("\n");
+#endif
+
+  switch (object->type) {
+    case OBJ_CLOSURE: {
+      ObjClosure *closure = (ObjClosure *) object;
+      markObject((Obj *) closure->function);
+      for (int i = 0; i < closure->upvalueCount; i++) {
+        markObject((Obj *) closure->upvalues[i]);
+      }
+      break;
+    }
+    case OBJ_FUNCTION: {
+      ObjFunction *function = (ObjFunction *) object;
+      markObject((Obj *) function->name);
+      markArray(&function->chunk.constants);
+      break;
+    }
+    case OBJ_UPVALUE:
+      markValue(((ObjUpvalue *) object)->closed);
+      break;
+    case OBJ_NATIVE:
+    case OBJ_STRING:
+      break;
+  }
+}
+void traceReferences() {
+  while (vm.grayCount > 0) {
+    Obj *object = vm.grayStack[--vm.grayCount];
+    blackenObject(object);
+  }
+}
+void sweep() {
+  Obj *previous = NULL;
+  Obj *object = vm.objects;
+  while (object != NULL) {
+    if (object->isMarked) {
+      object->isMarked = false;
+      previous = object;
+      object = object->next;
+    } else {
+      Obj *unreached = object;
+      object = object->next;
+      if (previous != NULL) {
+        previous->next = object;
+      } else {
+        vm.objects = object;
+      }
+
+      freeObject(unreached);
+    }
+  }
+}
 void collectGarbage() {
 #ifdef DEBUG_LOG_GC
   printf("-- gc begin\n");
+  size_t before = vm.bytesAllocated;
 #endif
 
   markRoots();
+  traceReferences();
+  tableRemoveWhite(&vm.strings);
+  sweep();// sweeping unused objects
+
+  vm.nextGC = vm.bytesAllocated * GC_HEAP_GROW_FACTOR;
 
 #ifdef DEBUG_LOG_GC
   printf("-- gc end\n");
+  printf("   collected %zu bytes (from %zu to %zu) next at %zu\n",
+         before - vm.bytesAllocated, before, vm.bytesAllocated, vm.nextGC);
 #endif
 }
 void markValue(Value value) {
   if (IS_OBJ(value)) markObject(AS_OBJ(value));
 }
+static void markArray(ValueArray *array) {
+  for (int i = 0; i < array->count; i++) {
+    markValue(array->values[i]);
+  }
+}
 void markObject(Obj *object) {
   if (object == NULL) return;
+  if (object->isMarked) return;
 #ifdef DEBUG_LOG_GC
   printf("%p mark ", (void *) object);
   printValue(OBJ_VAL(object));
@@ -116,4 +199,13 @@ void markObject(Obj *object) {
 #endif
 
   object->isMarked = true;
+
+  if (vm.grayCapacity < vm.grayCount + 1) {
+    vm.grayCapacity = GROW_CAPACITY(vm.grayCapacity);
+    vm.grayStack = (Obj **) realloc(vm.grayStack, sizeof(Obj *) * vm.grayCapacity);
+
+    if (vm.grayStack == NULL) exit(1);
+  }
+
+  vm.grayStack[vm.grayCount++] = object;
 }

+ 3 - 0
object.c

@@ -27,7 +27,10 @@ static ObjString *allocateString(char *chars, int length,
   string->length = length;
   string->chars = chars;
   string->hash = hash;
+  push(OBJ_VAL(string));
   tableSet(&vm.strings, string, NIL_VAL);
+  pop();
+
   return string;
 }
 

+ 8 - 0
table.c

@@ -151,3 +151,11 @@ void markTable(Table *table) {
     markValue(entry->value);
   }
 }
+void tableRemoveWhite(Table *table) {
+  for (int i = 0; i < table->capacity; i++) {
+    Entry *entry = &table->entries[i];
+    if (entry->key != NULL && !entry->key->obj.isMarked) {
+      tableDelete(table, entry->key);
+    }
+  }
+}

+ 1 - 1
table.h

@@ -38,7 +38,7 @@ void tableAddAll(Table *from, Table *to);
 /// \param hash uint32_t
 /// \return  ObjString*
 ObjString *tableFindString(Table *table, const char *chars, int length, uint32_t hash);
-
+void tableRemoveWhite(Table *table);
 void markTable(Table *table);
 
 #endif//CLOX_TABLE_H

+ 11 - 2
vm.c

@@ -61,6 +61,13 @@ static Value clockNative(int argCount, Value *args) {
 void initVM() {
   resetStack();
   vm.objects = NULL;
+  vm.bytesAllocated = 0;
+  vm.nextGC = 1024 * 1024;
+
+  vm.grayCount = 0;
+  vm.grayCapacity = 0;
+  vm.grayStack = NULL;
+
   initTable(&vm.globals);
   initTable(&vm.strings);
 
@@ -113,8 +120,8 @@ static bool isFalse(Value value) {
   return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
 }
 static void concatenate() {
-  ObjString *b = AS_STRING(pop());
-  ObjString *a = AS_STRING(pop());
+  ObjString *b = AS_STRING(peek(0));
+  ObjString *a = AS_STRING(peek(1));
 
   int length = a->length + b->length;
   char *chars = ALLOCATE(char, length + 1);
@@ -123,6 +130,8 @@ static void concatenate() {
   chars[length] = '\0';
 
   ObjString *result = takeString(chars, length);
+  pop();
+  pop();
   push(OBJ_VAL(result));
 }
 static ObjUpvalue *captureUpvalue(Value *local) {

+ 6 - 0
vm.h

@@ -33,7 +33,13 @@ typedef struct VM {
   Table globals;
   Table strings;//
   ObjUpvalue *openUpvalues;
+
+  size_t bytesAllocated;
+  size_t nextGC;
   Obj *objects;// 管理分配的 heap 内存
+  int grayCount;
+  int grayCapacity;
+  Obj** grayStack;
 } VM;
 typedef enum {
   INTERPRET_OK,