memory.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. #include "memory.h"
  2. #include "vm.h"
  3. #ifdef DEBUG_LOG_GC
  4. #include "debug.h"
  5. #include <stdlib.h>
  6. #endif
  7. #define GC_HEAP_GROW_FACTOR 2
  8. static void markArray(ValueArray *);
  9. // oldSize newSize Operation
  10. // 0 Non‑zero Allocate new block.
  11. // Non‑zero 0 Free allocation.
  12. // Non‑zero Smaller than oldSize Shrink existing allocation.
  13. // Non‑zero Larger than oldSize Grow existing allocation.
  14. void *reallocate(void *pointer, size_t oldSize, size_t newSize) {
  15. vm.bytesAllocated += newSize - oldSize;
  16. if (newSize > oldSize) {
  17. #ifdef DEBUG_STRESS_GC
  18. collectGarbage();
  19. #endif
  20. if (vm.bytesAllocated > vm.nextGC) {
  21. collectGarbage();
  22. }
  23. }
  24. if (newSize == 0) {
  25. free(pointer);
  26. return NULL;
  27. }
  28. /*
  29. The realloc() function tries to change the size of the allocation pointed
  30. to by ptr to size, and returns ptr. If there is not enough room to
  31. enlarge the memory allocation pointed to by ptr, realloc() creates a new
  32. allocation, copies as much of the old data pointed to by ptr as will fit
  33. to the new allocation, frees the old allocation, and returns a pointer to
  34. the allocated memory. If ptr is NULL, realloc() is identical to a call
  35. to malloc() for size bytes. If size is zero and ptr is not NULL, a new,
  36. minimum sized object is allocated and the original object is freed. When
  37. extending a region allocated with calloc(3), realloc(3) does not guaran-
  38. tee that the additional memory is also zero-filled
  39. */
  40. void *result = realloc(pointer, newSize);
  41. if (result == NULL) exit(1);
  42. return result;
  43. }
  44. static void freeObject(Obj *object) {
  45. #ifdef DEBUG_LOG_GC
  46. PRINTLNF("%p free type %d", (void *) object, object->type);
  47. #endif
  48. switch (object->type) {
  49. case OBJ_NATIVE:
  50. FREE(ObjNative, object);
  51. break;
  52. case OBJ_FUNCTION: {
  53. ObjFunction *function = (ObjFunction *) object;
  54. freeChunk(&function->chunk);
  55. FREE(ObjFunction, object);
  56. break;
  57. }
  58. case OBJ_STRING:
  59. FREE_ARRAY(char, ((ObjString *) object)->chars, ((ObjString *) object)->length + 1);
  60. FREE(ObjString, object);
  61. break;
  62. case OBJ_CLOSURE: {
  63. ObjClosure *closure = (ObjClosure *) object;
  64. FREE_ARRAY(ObjUpvalue *, closure->upvalues, closure->upvalueCount);
  65. FREE(ObjClosure, object);
  66. break;
  67. }
  68. case OBJ_UPVALUE:
  69. FREE(ObjUpvalue, object);
  70. break;
  71. case OBJ_CLASS: {
  72. ObjClass *klass = (ObjClass *) object;
  73. freeTable(&klass->methods);
  74. FREE(ObjClass, object);
  75. break;
  76. }
  77. case OBJ_INSTANCE: {
  78. ObjInstance *instance = (ObjInstance *) object;
  79. freeTable(&instance->fields);
  80. FREE(ObjInstance, object);
  81. break;
  82. }
  83. case OBJ_BOUND_METHOD:
  84. FREE(ObjBoundMethod, object);
  85. break;
  86. }
  87. }
  88. void freeObjects() {
  89. Obj *object = vm.objects;
  90. while (object != NULL) {
  91. Obj *next = object->next;
  92. freeObject(object);
  93. object = next;
  94. }
  95. free(vm.grayStack);
  96. }
  97. static void markRoots() {
  98. for (Value *slot = vm.stack; slot < vm.stackTop; slot++) {
  99. markValue(*slot);
  100. }
  101. for (int i = 0; i < vm.frameCount; i++) {
  102. markObject((Obj *) vm.frames[i].closure);
  103. }
  104. for (ObjUpvalue *upvalue = vm.openUpvalues;
  105. upvalue != NULL; upvalue = upvalue->next) {
  106. markObject((Obj *) upvalue);
  107. }
  108. markTable(&vm.globals);
  109. markCompilerRoots();
  110. markObject((Obj *) vm.initString);
  111. }
  112. void blackenObject(Obj *object) {
  113. #ifdef DEBUG_LOG_GC
  114. printf("%p blacken ", (void *) object);
  115. printValue(OBJ_VAL(object));
  116. printf("\n");
  117. #endif
  118. switch (object->type) {
  119. case OBJ_INSTANCE: {
  120. ObjInstance *instance = (ObjInstance *) object;
  121. markObject((Obj *) instance->klass);
  122. markTable(&instance->fields);
  123. break;
  124. }
  125. case OBJ_CLASS: {
  126. ObjClass *klass = (ObjClass *) object;
  127. markObject((Obj *) klass->name);
  128. markTable(&klass->methods);
  129. break;
  130. }
  131. case OBJ_CLOSURE: {
  132. ObjClosure *closure = (ObjClosure *) object;
  133. markObject((Obj *) closure->function);
  134. for (int i = 0; i < closure->upvalueCount; i++) {
  135. markObject((Obj *) closure->upvalues[i]);
  136. }
  137. break;
  138. }
  139. case OBJ_FUNCTION: {
  140. ObjFunction *function = (ObjFunction *) object;
  141. markObject((Obj *) function->name);
  142. markArray(&function->chunk.constants);
  143. break;
  144. }
  145. case OBJ_UPVALUE:
  146. markValue(((ObjUpvalue *) object)->closed);
  147. break;
  148. case OBJ_NATIVE:
  149. case OBJ_STRING:
  150. break;
  151. case OBJ_BOUND_METHOD: {
  152. ObjBoundMethod *bound = (ObjBoundMethod *) object;
  153. markValue(bound->receiver);
  154. markObject((Obj *) bound->method);
  155. break;
  156. }
  157. }
  158. }
  159. void traceReferences() {
  160. while (vm.grayCount > 0) {
  161. Obj *object = vm.grayStack[--vm.grayCount];
  162. blackenObject(object);
  163. }
  164. }
  165. void sweep() {
  166. Obj *previous = NULL;
  167. Obj *object = vm.objects;
  168. while (object != NULL) {
  169. if (object->isMarked) {
  170. object->isMarked = false;
  171. previous = object;
  172. object = object->next;
  173. } else {
  174. Obj *unreached = object;
  175. object = object->next;
  176. if (previous != NULL) {
  177. previous->next = object;
  178. } else {
  179. vm.objects = object;
  180. }
  181. freeObject(unreached);
  182. }
  183. }
  184. }
  185. void collectGarbage() {
  186. #ifdef DEBUG_LOG_GC
  187. printf("-- gc begin\n");
  188. size_t before = vm.bytesAllocated;
  189. #endif
  190. markRoots();
  191. traceReferences();
  192. tableRemoveWhite(&vm.strings);
  193. sweep();// sweeping unused objects
  194. vm.nextGC = vm.bytesAllocated * GC_HEAP_GROW_FACTOR;
  195. #ifdef DEBUG_LOG_GC
  196. printf("-- gc end\n");
  197. printf(" collected %zu bytes (from %zu to %zu) next at %zu\n",
  198. before - vm.bytesAllocated, before, vm.bytesAllocated, vm.nextGC);
  199. #endif
  200. }
  201. void markValue(Value value) {
  202. if (IS_OBJ(value)) markObject(AS_OBJ(value));
  203. }
  204. static void markArray(ValueArray *array) {
  205. for (int i = 0; i < array->count; i++) {
  206. markValue(array->values[i]);
  207. }
  208. }
  209. void markObject(Obj *object) {
  210. if (object == NULL) return;
  211. if (object->isMarked) return;
  212. #ifdef DEBUG_LOG_GC
  213. printf("%p mark ", (void *) object);
  214. printValue(OBJ_VAL(object));
  215. printf("\n");
  216. #endif
  217. object->isMarked = true;
  218. if (vm.grayCapacity < vm.grayCount + 1) {
  219. vm.grayCapacity = GROW_CAPACITY(vm.grayCapacity);
  220. vm.grayStack = (Obj **) realloc(vm.grayStack, sizeof(Obj *) * vm.grayCapacity);
  221. if (vm.grayStack == NULL) exit(1);
  222. }
  223. vm.grayStack[vm.grayCount++] = object;
  224. }