📄 gc-incremental.c
字号:
STOPWORLD(); /* measure time */ startTiming(&gc_time, "gctime-scan"); /* Walk all objects on the finalizer list */ for (unit = gclists[finalise].cnext; unit != &gclists[finalise]; unit = nunit) { nunit = unit->cnext; gcMarkObject(gcif, UTOMEM(unit)); } (*walkRootSet)(gcif);}/* * Finish off the GC process. Any unreached (white) objects are moved * for finalising and the finaliser woken. * The reached (black) objects are moved onto the now empty white list * and recoloured white. */staticvoidfinishGC(Collector *gcif){ gc_unit* unit; gc_block* info; int idx; /* There shouldn't be any grey objects at this point */ assert(gclists[grey].cnext == &gclists[grey]); /* * Any white objects should now be freed, but we cannot call * gc_heap_free here because we might block in gc_heap_free, * which would leave the white list unprotected. * So we move them to a 'mustfree' list from where we'll pull them * off later. * * XXX: this is so silly it hurts. Jason has a fix. */ while (gclists[white].cnext != &gclists[white]) { unit = gclists[white].cnext; UREMOVELIST(unit); info = GCMEM2BLOCK(unit); idx = GCMEM2IDX(info, unit); assert(GC_GET_COLOUR(info, idx) == GC_COLOUR_WHITE); assert(GC_GET_STATE(info, idx) == GC_STATE_NORMAL); gcStats.freedmem += GCBLOCKSIZE(info); gcStats.freedobj += 1; UAPPENDLIST(gclists[mustfree], unit); OBJECTSTATSREMOVE(unit); } /* * Now move the black objects back to the white queue for next time. * Note that all objects that were eligible for finalization are now * black - this is so because we marked and then walked them. * We recognize them by their "INFINALIZE" state, however, and put * them on the finalise list. */ while (gclists[black].cnext != &gclists[black]) { unit = gclists[black].cnext; UREMOVELIST(unit); info = GCMEM2BLOCK(unit); idx = GCMEM2IDX(info, unit); assert(GC_GET_COLOUR(info, idx) == GC_COLOUR_BLACK); if (GC_GET_STATE(info, idx) == GC_STATE_INFINALIZE) { gcStats.finalmem += GCBLOCKSIZE(info); gcStats.finalobj += 1; UAPPENDLIST(gclists[finalise], unit); } else { UAPPENDLIST(gclists[white], unit); } GC_SET_COLOUR(info, idx, GC_COLOUR_WHITE); } /* this is where we'll stop locking out other threads * measure gc time until here. This is not quite accurate, as * it excludes the time to sweep objects, but lacking * per-thread timing it's a reasonable thing to do. */ stopTiming(&gc_time); /* * Now that all lists that the mutator manipulates are in a * consistent state, we can reenable the mutator here */ RESUMEWORLD(); /* * Now free the objects. We can block here since we're the only * thread manipulating the "mustfree" list. */ startTiming(&sweep_time, "gctime-sweep"); while (gclists[mustfree].cnext != &gclists[mustfree]) { destroy_func_t destroy; unit = gclists[mustfree].cnext; /* invoke destroy function before freeing the object */ info = GCMEM2BLOCK(unit); idx = GCMEM2IDX(info, unit); destroy = gcFunctions[GC_GET_FUNCS(info,idx)].destroy; if (destroy != 0) { destroy(gcif, UTOMEM(unit)); } UREMOVELIST(unit); addToCounter(&gcgcablemem, "gcmem-gcable objects", 1, -((jlong)GCBLOCKSIZE(info))); gc_heap_free(unit); } stopTiming(&sweep_time);}staticvoidstartFinalizer(void){ int iLockRoot; int start; start = 0; lockStaticMutex(&gc_lock); /* If there's stuff to be finalised then we'd better do it */ if (gclists[finalise].cnext != &gclists[finalise]) { start = 1; } unlockStaticMutex(&gc_lock); lockStaticMutex(&finman); if (start != 0 && finalRunning == false) { finalRunning = true; signalStaticCond(&finman); } unlockStaticMutex(&finman);}/* * The finaliser sits in a loop waiting to finalise objects. When a * new finalised list is available, it is woken by the GC and finalises * the objects in turn. An object is only finalised once after which * it is deleted. */static voidfinaliserMan(void* arg){ gc_block* info; gc_unit* unit; int idx; Collector *gcif = (Collector*)arg; int iLockRoot; for (;;) { lockStaticMutex(&finman); finalRunning = false; while (finalRunning == false) { waitStaticCond(&finman, 0); } assert(finalRunning == true); while (gclists[finalise].cnext != &gclists[finalise]) { lockStaticMutex(&gc_lock); unit = gclists[finalise].cnext; UREMOVELIST(unit); UAPPENDLIST(gclists[grey], unit); info = GCMEM2BLOCK(unit); idx = GCMEM2IDX(info, unit); gcStats.finalmem -= GCBLOCKSIZE(info); gcStats.finalobj -= 1; assert(GC_GET_STATE(info,idx) == GC_STATE_INFINALIZE); /* Objects are only finalised once */ GC_SET_STATE(info, idx, GC_STATE_FINALIZED); GC_SET_COLOUR(info, idx, GC_COLOUR_GREY); unlockStaticMutex(&gc_lock); /* Call finaliser */ unlockStaticMutex(&finman); (*gcFunctions[GC_GET_FUNCS(info,idx)].final)(gcif, UTOMEM(unit)); lockStaticMutex(&finman); } /* Wake up anyone waiting for the finalizer to finish */ broadcastStaticCond(&finman); unlockStaticMutex(&finman); }}/* * Explicity invoke the garbage collector and wait for it to complete. */staticvoidgcInvokeGC(Collector* gcif, int mustgc){ int iLockRoot; lockStaticMutex(&gcman); if (gcRunning == 0) { gcRunning = mustgc ? 2 : 1; signalStaticCond(&gcman); } unlockStaticMutex(&gcman); lockStaticMutex(&gcman); while (gcRunning != 0) { waitStaticCond(&gcman, 0); } unlockStaticMutex(&gcman);}/* * GC and invoke the finalizer. Used to run finalizers on exit. */staticvoidgcInvokeFinalizer(Collector* gcif){ int iLockRoot; /* First invoke the GC */ GC_invoke(gcif, 1); /* Run the finalizer (if might already be running as a result of * the GC) */ lockStaticMutex(&finman); if (finalRunning == false) { finalRunning = true; signalStaticCond(&finman); } waitStaticCond(&finman, 0); unlockStaticMutex(&finman);}/* * Allocate a new object. The object is attached to the white queue. * After allocation, if incremental collection is active we peform * a little garbage collection. If we finish it, we wakeup the garbage * collector. */void throwOutOfMemory(void) __NORETURN__;staticvoid*gcMalloc(Collector* gcif, size_t size, int fidx){ gc_block* info; gc_unit* unit; void * volatile mem; /* needed on SGI, see comment below */ int i; size_t bsz; int iLockRoot; assert(gc_init != 0); assert(fidx < nrTypes && size != 0); unit = gc_heap_malloc(size + sizeof(gc_unit)); /* keep pointer to object */ mem = UTOMEM(unit); if (unit == 0) { return 0; } lockStaticMutex(&gc_lock); info = GCMEM2BLOCK(mem); i = GCMEM2IDX(info, unit); bsz = GCBLOCKSIZE(info); gcStats.totalmem += bsz; gcStats.totalobj += 1; gcStats.allocmem += bsz; gcStats.allocobj += 1; GC_SET_FUNCS(info, i, fidx); OBJECTSTATSADD(unit); /* Determine whether we need to finalise or not */ if (gcFunctions[fidx].final == GC_OBJECT_NORMAL || gcFunctions[fidx].final == GC_OBJECT_FIXED) { GC_SET_STATE(info, i, GC_STATE_NORMAL); } else { GC_SET_STATE(info, i, GC_STATE_NEEDFINALIZE); } /* If object is fixed, we give it the fixed colour and do not * attach it to any lists. This object is not part of the GC * regime and must be freed explicitly. */ if (gcFunctions[fidx].final == GC_OBJECT_FIXED) { addToCounter(&gcfixedmem, "gcmem-fixed objects", 1, bsz); GC_SET_COLOUR(info, i, GC_COLOUR_FIXED); } else { addToCounter(&gcgcablemem, "gcmem-gcable objects", 1, bsz); /* * Note that as soon as we put the object on the white list, * the gc might come along and free the object if it can't * find any references to it. This is why we need to keep * a reference in `mem'. Note that keeping a reference in * `unit' will not do because markObject performs a UTOUNIT()! * In addition, on some architectures (SGI), we must tell the * compiler to not delay computing mem by defining it volatile. */ GC_SET_COLOUR(info, i, GC_COLOUR_WHITE); UAPPENDLIST(gclists[white], unit); } if (!reserve) { reserve = gc_primitive_reserve(); } /* It is not safe to allocate java objects the first time * gcMalloc is called, but it should be safe after gcEnable * has been called. */ if (garbageman && !outOfMem && !outOfMem_allocator) { outOfMem_allocator = jthread_current(); } unlockStaticMutex(&gc_lock); /* jthread_current() will be null in some window before we * should try allocating java objects */ if (!outOfMem && outOfMem_allocator && outOfMem_allocator == jthread_current()) { outOfMem = OOM_ALLOCATING; outOfMem = OutOfMemoryError; /* implicit allocation */ outOfMem_allocator = 0; gc_add_ref(outOfMem); } return (mem);}staticHjava_lang_Throwable *gcThrowOOM(Collector *gcif){ Hjava_lang_Throwable *ret = 0; int reffed; int iLockRoot; /* * Make sure we are the only thread to use this exception * object. */ lockStaticMutex(&gc_lock); ret = outOfMem; reffed = outOfMem != 0; outOfMem = 0; /* We try allocating reserved pages before we allocate the * outOfMemory error. We can use some or all of the reserved * pages to actually grab an error. */ if (reserve) { gc_primitive_free(reserve); reserve = 0; if (!ret || ret == OOM_ALLOCATING) { unlockStaticMutex(&gc_lock); ret = OutOfMemoryError; /* implicit allocation */ lockStaticMutex(&gc_lock); } } if (ret == OOM_ALLOCATING || ret == 0) { /* die now */ unlockStaticMutex(&gc_lock); dprintf( "Not enough memory to throw OutOfMemoryError!\n"); ABORT(); } unlockStaticMutex(&gc_lock); if (reffed) gc_rm_ref(ret); return ret;}/* * Reallocate an object. */staticvoid*gcRealloc(Collector* gcif, void* mem, size_t size, int fidx){ gc_block* info; int idx; void* newmem; gc_unit* unit; int osize; int iLockRoot; assert(gcFunctions[fidx].final == GC_OBJECT_FIXED); /* If nothing to realloc from, just allocate */ if (mem == NULL) { return (gcMalloc(gcif, size, fidx)); } lockStaticMutex(&gc_lock); unit = UTOUNIT(mem); info = GCMEM2BLOCK(unit); idx = GCMEM2IDX(info, unit); osize = GCBLOCKSIZE(info) - sizeof(gc_unit); /* Can only handled fixed objects at the moment */ assert(GC_GET_COLOUR(info, idx) == GC_COLOUR_FIXED); info = 0; unlockStaticMutex(&gc_lock); /* If we'll fit into the current space, just send it back */ if (osize >= size) { return (mem); } /* Allocate new memory, copy data, and free the old */ newmem = gcMalloc(gcif, size, fidx); memcpy(newmem, mem, osize); gcFree(gcif, mem); return (newmem);}/* * Explicitly free an object. */staticvoidgcFree(Collector* gcif, void* mem){ gc_block* info; int idx; gc_unit* unit; int iLockRoot; if (mem != 0) { lockStaticMutex(&gc_lock); unit = UTOUNIT(mem); info = GCMEM2BLOCK(unit); idx = GCMEM2IDX(info, unit); if (GC_GET_COLOUR(info, idx) == GC_COLOUR_FIXED) { size_t sz = GCBLOCKSIZE(info); OBJECTSTATSREMOVE(unit); /* Keep the stats correct */ gcStats.totalmem -= sz; gcStats.totalobj -= 1; addToCounter(&gcfixedmem, "gcmem-fixed objects", 1, -(jlong)sz); gc_heap_free(unit); } else { assert(!!!"Attempt to explicitly free nonfixed object"); } unlockStaticMutex(&gc_lock); }}staticvoidgcInit(Collector *collector){ gc_init = 1;}/* * Start gc threads, which enable collection */static void/* ARGSUSED */gcEnable(Collector* collector){ errorInfo info; if (DBGEXPR(NOGC, false, true)) { /* Start the GC daemons we need */ finalman = createDaemon(&finaliserMan, "finaliser", collector, THREAD_MAXPRIO, FINALIZERSTACKSIZE, &info); garbageman = createDaemon(&gcMan, "gc", collector, THREAD_MAXPRIO, GCSTACKSIZE, &info); assert(finalman && garbageman); }}#if defined(SUPPORT_VERBOSEMEM)/* --------------------------------------------------------------------- *//* The following functions are strictly for statistics gathering */staticvoidobjectStatsChange(gc_unit* unit, int diff){ gc_block* info; int idx; info = GCMEM2BLOCK(unit); idx = GC_GET_FUNCS(info, GCMEM2IDX(info, unit)); assert(idx >= 0 && idx < nrTypes); gcFunctions[idx].nr += diff * 1; gcFunctions[idx].mem += diff * GCBLOCKSIZE(info);}static voidobjectStatsPrint(void){ int cnt = 0; dprintf("Memory statistics:\n"); dprintf("------------------\n"); while (cnt < nrTypes) { dprintf("%14.14s: Nr %6d Mem %6dK", gcFunctions[cnt].description, gcFunctions[cnt].nr, gcFunctions[cnt].mem/1024); if (++cnt % 2 != 0) { dprintf(" "); } else { dprintf("\n"); } } if (cnt % 2 != 0) { dprintf("\n"); }}#endif/* * vtable for object implementing the collector interface. */static struct GarbageCollectorInterface_Ops GC_Ops = { 0, /* reserved */ 0, /* reserved */ 0, /* reserved */ gcMalloc, gcRealloc, gcFree, gcInvokeGC, gcInvokeFinalizer, gcInit, gcEnable, gcMarkAddress, gcMarkObject, gcGetObjectSize, gcGetObjectDescription, gcGetObjectIndex, gcGetObjectBase, gcWalkMemory, gcWalkConservative, gcRegisterFixedTypeByIndex, gcRegisterGcTypeByIndex, gcThrowOOM};/* * Initialise the Garbage Collection system. */Collector* createGC(void (*_walkRootSet)(Collector*)){ walkRootSet = _walkRootSet; URESETLIST(gclists[white]); URESETLIST(gclists[grey]); URESETLIST(gclists[black]); URESETLIST(gclists[finalise]); URESETLIST(gclists[mustfree]); gc_obj.collector.ops = &GC_Ops; return (&gc_obj.collector);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -