void setGCStateCurrentHeap (GC_state s, size_t oldGenBytesRequested, size_t nurseryBytesRequested) { GC_heap h; pointer nursery; size_t nurserySize; pointer genNursery; size_t genNurserySize; if (DEBUG_DETAILED) fprintf (stderr, "setGCStateCurrentHeap(%s, %s)\n", uintmaxToCommaString(oldGenBytesRequested), uintmaxToCommaString(nurseryBytesRequested)); h = &s->heap; assert (isFrontierAligned (s, h->start + h->oldGenSize + oldGenBytesRequested)); s->limitPlusSlop = h->start + h->size; s->limit = s->limitPlusSlop - GC_HEAP_LIMIT_SLOP; nurserySize = h->size - (h->oldGenSize + oldGenBytesRequested); assert (isFrontierAligned (s, s->limitPlusSlop - nurserySize)); nursery = s->limitPlusSlop - nurserySize; genNursery = alignFrontier (s, s->limitPlusSlop - (nurserySize / 2)); genNurserySize = (size_t)(s->limitPlusSlop - genNursery); if (/* The mutator marks cards. */ s->mutatorMarksCards /* There is enough space in the generational nursery. */ and (nurseryBytesRequested <= genNurserySize) /* The nursery is large enough to be worth it. */ and (((float)(h->size - s->lastMajorStatistics.bytesLive) / (float)nurserySize) <= s->controls.ratios.nursery) and /* There is a reason to use generational GC. */ ( /* We must use it for debugging purposes. */ FORCE_GENERATIONAL /* We just did a mark compact, so it will be advantageous to to use it. */ or (s->lastMajorStatistics.kind == GC_MARK_COMPACT) /* The live ratio is low enough to make it worthwhile. */ or ((float)h->size / (float)s->lastMajorStatistics.bytesLive <= (h->withMapsSize < s->sysvals.ram ? s->controls.ratios.copyGenerational : s->controls.ratios.markCompactGenerational)) )) { s->canMinor = TRUE; nursery = genNursery; nurserySize = genNurserySize; clearCardMap (s); } else { unless (nurseryBytesRequested <= nurserySize) die ("Out of memory. Insufficient space in nursery."); s->canMinor = FALSE; } assert (nurseryBytesRequested <= nurserySize); s->heap.nursery = nursery; s->frontier = nursery; assert (nurseryBytesRequested <= (size_t)(s->limitPlusSlop - s->frontier)); assert (isFrontierAligned (s, s->heap.nursery)); assert (hasHeapBytesFree (s, oldGenBytesRequested, nurseryBytesRequested)); }
void minorCheneyCopyGC (GC_state s) { size_t bytesAllocated; size_t bytesCopied; struct rusage ru_start; if (DEBUG_GENERATIONAL) fprintf (stderr, "minorGC nursery = "FMTPTR" frontier = "FMTPTR"\n", (uintptr_t)s->heap.nursery, (uintptr_t)s->frontier); assert (invariantForGC (s)); bytesAllocated = s->frontier - s->heap.nursery; if (bytesAllocated == 0) return; s->cumulativeStatistics.bytesAllocated += bytesAllocated; if (not s->canMinor) { s->heap.oldGenSize += bytesAllocated; bytesCopied = 0; } else { if (detailedGCTime (s)) startTiming (&ru_start); s->cumulativeStatistics.numMinorGCs++; s->forwardState.amInMinorGC = TRUE; if (DEBUG_GENERATIONAL or s->controls.messages) { fprintf (stderr, "[GC: Starting minor Cheney-copy;]\n"); fprintf (stderr, "[GC:\tfrom nursery at "FMTPTR" of size %s bytes.]\n", (uintptr_t)(s->heap.nursery), uintmaxToCommaString(bytesAllocated)); } s->forwardState.toStart = s->heap.start + s->heap.oldGenSize; assert (isFrontierAligned (s, s->forwardState.toStart)); s->forwardState.toLimit = s->forwardState.toStart + bytesAllocated; assert (invariantForGC (s)); s->forwardState.back = s->forwardState.toStart; /* Forward all globals. Would like to avoid doing this once all * the globals have been assigned. */ foreachGlobalObjptr (s, forwardObjptrIfInNursery); forwardInterGenerationalObjptrs (s); foreachObjptrInRange (s, s->forwardState.toStart, &s->forwardState.back, forwardObjptrIfInNursery, TRUE); updateWeaksForCheneyCopy (s); bytesCopied = s->forwardState.back - s->forwardState.toStart; s->cumulativeStatistics.bytesCopiedMinor += bytesCopied; s->heap.oldGenSize += bytesCopied; s->lastMajorStatistics.numMinorGCs++; if (detailedGCTime (s)) stopTiming (&ru_start, &s->cumulativeStatistics.ru_gcMinor); if (DEBUG_GENERATIONAL or s->controls.messages) fprintf (stderr, "[GC: Finished minor Cheney-copy; copied %s bytes.]\n", uintmaxToCommaString(bytesCopied)); } }
void majorCheneyCopyGC (GC_state s) { size_t bytesCopied; struct rusage ru_start; pointer toStart; assert (s->secondaryHeap.size >= s->heap.oldGenSize); if (detailedGCTime (s)) startTiming (&ru_start); s->cumulativeStatistics.numCopyingGCs++; s->forwardState.amInMinorGC = FALSE; if (DEBUG or s->controls.messages) { fprintf (stderr, "[GC: Starting major Cheney-copy;]\n"); fprintf (stderr, "[GC:\tfrom heap at "FMTPTR" of size %s bytes,]\n", (uintptr_t)(s->heap.start), uintmaxToCommaString(s->heap.size)); fprintf (stderr, "[GC:\tto heap at "FMTPTR" of size %s bytes.]\n", (uintptr_t)(s->secondaryHeap.start), uintmaxToCommaString(s->secondaryHeap.size)); } s->forwardState.toStart = s->secondaryHeap.start; s->forwardState.toLimit = s->secondaryHeap.start + s->secondaryHeap.size; assert (s->secondaryHeap.start != (pointer)NULL); /* The next assert ensures there is enough space for the copy to * succeed. It does not assert * (s->secondaryHeap.size >= s->heap.size) * because that is too strong. */ assert (s->secondaryHeap.size >= s->heap.oldGenSize); toStart = alignFrontier (s, s->secondaryHeap.start); s->forwardState.back = toStart; foreachGlobalObjptr (s, forwardObjptr); foreachObjptrInRange (s, toStart, &s->forwardState.back, forwardObjptr, TRUE); updateWeaksForCheneyCopy (s); s->secondaryHeap.oldGenSize = s->forwardState.back - s->secondaryHeap.start; bytesCopied = s->secondaryHeap.oldGenSize; s->cumulativeStatistics.bytesCopied += bytesCopied; swapHeapsForCheneyCopy (s); s->lastMajorStatistics.kind = GC_COPYING; if (detailedGCTime (s)) stopTiming (&ru_start, &s->cumulativeStatistics.ru_gcCopying); if (DEBUG or s->controls.messages) fprintf (stderr, "[GC: Finished major Cheney-copy; copied %s bytes.]\n", uintmaxToCommaString(bytesCopied)); }
bool hasHeapBytesFree (GC_state s, size_t oldGen, size_t nursery) { size_t total; bool res; total = s->heap.oldGenSize + oldGen + (s->canMinor ? 2 : 1) * (size_t)(s->limitPlusSlop - s->heap.nursery); res = (total <= s->heap.size) and (nursery <= (size_t)(s->limitPlusSlop - s->frontier)); if (DEBUG_DETAILED) fprintf (stderr, "%s = hasBytesFree (%s, %s)\n", boolToString (res), uintmaxToCommaString(oldGen), uintmaxToCommaString(nursery)); return res; }
void growStackCurrent (GC_state s) { size_t reserved; GC_stack stack; reserved = sizeofStackGrowReserved (s, getStackCurrent(s)); if (DEBUG_STACKS or s->controls.messages) fprintf (stderr, "[GC: Growing stack of size %s bytes to size %s bytes, using %s bytes.]\n", uintmaxToCommaString(getStackCurrent(s)->reserved), uintmaxToCommaString(reserved), uintmaxToCommaString(getStackCurrent(s)->used)); assert (hasHeapBytesFree (s, sizeofStackWithHeader (s, reserved), 0)); stack = newStack (s, reserved, TRUE); copyStack (s, getStackCurrent(s), stack); getThreadCurrent(s)->stack = pointerToObjptr ((pointer)stack, s->heap.start); markCard (s, objptrToPointer (getThreadCurrentObjptr(s), s->heap.start)); }
void *GC_mmapAnon_safe (void *p, size_t length) { void *result; result = GC_mmapAnon (p, length); if ((void*)-1 == result) { GC_displayMem (); die ("Out of memory. Unable to allocate %s bytes.\n", uintmaxToCommaString(length)); } return result; }
/* translateRange: Translate the range from <base> to <base + size> from * address <from> to address <to> */ void translateRange (GC_state s, pointer base, pointer from, pointer to, size_t size) { pointer limit; if (from == to) return; if (DEBUG or s->controls.messages) fprintf (stderr, "[GC: Translating range at "FMTPTR" of size %s bytes from "FMTPTR" to "FMTPTR".]\n", (uintptr_t)base, uintmaxToCommaString(size), (uintptr_t)from, (uintptr_t)to); s->translateState.from = from; s->translateState.to = to; limit = base + size; foreachObjptrInRange (s, alignFrontier (s, base), &limit, translateObjptr, FALSE); }
/* translateHeap (s, from, to, size) */ void translateHeap (GC_state s, pointer from, pointer to, size_t size) { pointer limit; if (from == to) return; if (DEBUG or s->controls.messages) fprintf (stderr, "[GC: Translating old-gen of size %s bytes of heap at "FMTPTR" from "FMTPTR".]\n", uintmaxToCommaString(size), (uintptr_t)to, (uintptr_t)from); s->translateState.from = from; s->translateState.to = to; /* Translate globals and heap. */ foreachGlobalObjptr (s, translateObjptr); limit = to + size; foreachObjptrInRange (s, alignFrontier (s, to), &limit, translateObjptr, FALSE); }
int GC_init (GC_state s, int argc, char **argv) { char *worldFile; int res; assert (s->alignment >= GC_MODEL_MINALIGN); assert (isAligned (sizeof (struct GC_stack), s->alignment)); // While the following asserts are manifestly true, // they check the asserts in sizeofThread and sizeofWeak. assert (sizeofThread (s) == sizeofThread (s)); assert (sizeofWeak (s) == sizeofWeak (s)); s->amInGC = TRUE; s->amOriginal = TRUE; s->atomicState = 0; s->callFromCHandlerThread = BOGUS_OBJPTR; s->controls.fixedHeap = 0; s->controls.maxHeap = 0; s->controls.mayLoadWorld = TRUE; s->controls.mayPageHeap = FALSE; s->controls.mayProcessAtMLton = TRUE; s->controls.messages = FALSE; s->controls.oldGenSequenceSize = 0x100000; s->controls.ratios.copy = 4.0f; s->controls.ratios.copyGenerational = 4.0f; s->controls.ratios.grow = 8.0f; s->controls.ratios.hashCons = 0.0f; s->controls.ratios.live = 8.0f; s->controls.ratios.markCompact = 1.04f; s->controls.ratios.markCompactGenerational = 8.0f; s->controls.ratios.nursery = 10.0f; s->controls.ratios.ramSlop = 0.5f; s->controls.ratios.stackCurrentGrow = 2.0f; s->controls.ratios.stackCurrentMaxReserved = 32.0f; s->controls.ratios.stackCurrentPermitReserved = 4.0f; s->controls.ratios.stackCurrentShrink = 0.5f; s->controls.ratios.stackMaxReserved = 8.0f; s->controls.ratios.stackShrink = 0.5f; s->controls.summary = FALSE; s->controls.summaryFile = stderr; s->cumulativeStatistics.bytesAllocated = 0; s->cumulativeStatistics.bytesCopied = 0; s->cumulativeStatistics.bytesCopiedMinor = 0; s->cumulativeStatistics.bytesHashConsed = 0; s->cumulativeStatistics.bytesMarkCompacted = 0; s->cumulativeStatistics.bytesScannedMinor = 0; s->cumulativeStatistics.maxBytesLive = 0; s->cumulativeStatistics.maxHeapSize = 0; s->cumulativeStatistics.maxPauseTime = 0; s->cumulativeStatistics.maxStackSize = 0; s->cumulativeStatistics.numCardsMarked = 0; s->cumulativeStatistics.numCopyingGCs = 0; s->cumulativeStatistics.numHashConsGCs = 0; s->cumulativeStatistics.numMarkCompactGCs = 0; s->cumulativeStatistics.numMinorGCs = 0; rusageZero (&s->cumulativeStatistics.ru_gc); rusageZero (&s->cumulativeStatistics.ru_gcCopying); rusageZero (&s->cumulativeStatistics.ru_gcMarkCompact); rusageZero (&s->cumulativeStatistics.ru_gcMinor); s->currentThread = BOGUS_OBJPTR; s->hashConsDuringGC = FALSE; initHeap (s, &s->heap); s->lastMajorStatistics.bytesHashConsed = 0; s->lastMajorStatistics.bytesLive = 0; s->lastMajorStatistics.kind = GC_COPYING; s->lastMajorStatistics.numMinorGCs = 0; s->savedThread = BOGUS_OBJPTR; initHeap (s, &s->secondaryHeap); s->signalHandlerThread = BOGUS_OBJPTR; s->signalsInfo.amInSignalHandler = FALSE; s->signalsInfo.gcSignalHandled = FALSE; s->signalsInfo.gcSignalPending = FALSE; s->signalsInfo.signalIsPending = FALSE; sigemptyset (&s->signalsInfo.signalsHandled); sigemptyset (&s->signalsInfo.signalsPending); s->sysvals.pageSize = GC_pageSize (); s->sysvals.physMem = GC_physMem (); s->weaks = NULL; s->saveWorldStatus = true; initIntInf (s); initSignalStack (s); worldFile = NULL; unless (isAligned (s->sysvals.pageSize, CARD_SIZE)) die ("Page size must be a multiple of card size."); processAtMLton (s, 0, s->atMLtonsLength, s->atMLtons, &worldFile); res = processAtMLton (s, 1, argc, argv, &worldFile); if (s->controls.fixedHeap > 0 and s->controls.maxHeap > 0) die ("Cannot use both fixed-heap and max-heap."); unless (s->controls.ratios.markCompact <= s->controls.ratios.copy and s->controls.ratios.copy <= s->controls.ratios.live) die ("Ratios must satisfy mark-compact-ratio <= copy-ratio <= live-ratio."); unless (s->controls.ratios.stackCurrentPermitReserved <= s->controls.ratios.stackCurrentMaxReserved) die ("Ratios must satisfy stack-current-permit-reserved <= stack-current-max-reserved."); /* We align s->sysvals.ram by s->sysvals.pageSize so that we can * test whether or not we we are using mark-compact by comparing * heap size to ram size. If we didn't round, the size might be * slightly off. */ uintmax_t ram; ram = alignMax ((uintmax_t)(s->controls.ratios.ramSlop * (double)(s->sysvals.physMem)), (uintmax_t)(s->sysvals.pageSize)); ram = min (ram, alignMaxDown((uintmax_t)SIZE_MAX, (uintmax_t)(s->sysvals.pageSize))); s->sysvals.ram = (size_t)ram; if (DEBUG or DEBUG_RESIZING or s->controls.messages) fprintf (stderr, "[GC: Found %s bytes of RAM; using %s bytes (%.1f%% of RAM).]\n", uintmaxToCommaString(s->sysvals.physMem), uintmaxToCommaString(s->sysvals.ram), 100.0 * ((double)ram / (double)(s->sysvals.physMem))); if (DEBUG_SOURCES or DEBUG_PROFILE) { uint32_t i; for (i = 0; i < s->sourceMaps.frameSourcesLength; i++) { uint32_t j; uint32_t *sourceSeq; fprintf (stderr, "%"PRIu32"\n", i); sourceSeq = s->sourceMaps.sourceSeqs[s->sourceMaps.frameSources[i]]; for (j = 1; j <= sourceSeq[0]; j++) fprintf (stderr, "\t%s\n", s->sourceMaps.sourceNames[ s->sourceMaps.sources[sourceSeq[j]].sourceNameIndex ]); } } /* Initialize profiling. This must occur after processing * command-line arguments, because those may just be doing a * show-sources, in which case we don't want to initialize the * atExit. */ initProfiling (s); if (s->amOriginal) { initWorld (s); /* The mutator stack invariant doesn't hold, * because the mutator has yet to run. */ assert (invariantForMutator (s, TRUE, FALSE)); } else { loadWorldFromFileName (s, worldFile); if (s->profiling.isOn and s->profiling.stack) foreachStackFrame (s, enterFrameForProfiling); assert (invariantForMutator (s, TRUE, TRUE)); } s->amInGC = FALSE; return res; }
void performGC (GC_state s, size_t oldGenBytesRequested, size_t nurseryBytesRequested, bool forceMajor, bool mayResize) { uintmax_t gcTime; bool stackTopOk; size_t stackBytesRequested; struct rusage ru_start; size_t totalBytesRequested; enterGC (s); s->cumulativeStatistics.numGCs++; if (DEBUG or s->controls.messages) { size_t nurserySize = s->heap.size - ((size_t)(s->heap.nursery - s->heap.start)); size_t nurseryUsed = (size_t)(s->frontier - s->heap.nursery); fprintf (stderr, "[GC: Starting gc #%s; requesting %s nursery bytes and %s old-gen bytes,]\n", uintmaxToCommaString(s->cumulativeStatistics.numGCs), uintmaxToCommaString(nurseryBytesRequested), uintmaxToCommaString(oldGenBytesRequested)); fprintf (stderr, "[GC:\theap at "FMTPTR" of size %s bytes (+ %s bytes card/cross map),]\n", (uintptr_t)(s->heap.start), uintmaxToCommaString(s->heap.size), uintmaxToCommaString(s->heap.withMapsSize - s->heap.size)); fprintf (stderr, "[GC:\twith old-gen of size %s bytes (%.1f%% of heap),]\n", uintmaxToCommaString(s->heap.oldGenSize), 100.0 * ((double)(s->heap.oldGenSize) / (double)(s->heap.size))); fprintf (stderr, "[GC:\tand nursery of size %s bytes (%.1f%% of heap),]\n", uintmaxToCommaString(nurserySize), 100.0 * ((double)(nurserySize) / (double)(s->heap.size))); fprintf (stderr, "[GC:\tand nursery using %s bytes (%.1f%% of heap, %.1f%% of nursery).]\n", uintmaxToCommaString(nurseryUsed), 100.0 * ((double)(nurseryUsed) / (double)(s->heap.size)), 100.0 * ((double)(nurseryUsed) / (double)(nurserySize))); } assert (invariantForGC (s)); if (needGCTime (s)) startTiming (&ru_start); minorGC (s); stackTopOk = invariantForMutatorStack (s); stackBytesRequested = stackTopOk ? 0 : sizeofStackWithHeader (s, sizeofStackGrowReserved (s, getStackCurrent (s))); totalBytesRequested = oldGenBytesRequested + nurseryBytesRequested + stackBytesRequested; if (forceMajor or totalBytesRequested > s->heap.size - s->heap.oldGenSize) majorGC (s, totalBytesRequested, mayResize); setGCStateCurrentHeap (s, oldGenBytesRequested + stackBytesRequested, nurseryBytesRequested); assert (hasHeapBytesFree (s, oldGenBytesRequested + stackBytesRequested, nurseryBytesRequested)); unless (stackTopOk) growStackCurrent (s); setGCStateCurrentThreadAndStack (s); if (needGCTime (s)) { gcTime = stopTiming (&ru_start, &s->cumulativeStatistics.ru_gc); s->cumulativeStatistics.maxPauseTime = max (s->cumulativeStatistics.maxPauseTime, gcTime); } else gcTime = 0; /* Assign gcTime to quell gcc warning. */ if (DEBUG or s->controls.messages) { size_t nurserySize = s->heap.size - (size_t)(s->heap.nursery - s->heap.start); fprintf (stderr, "[GC: Finished gc #%s; time %s ms,]\n", uintmaxToCommaString(s->cumulativeStatistics.numGCs), uintmaxToCommaString(gcTime)); fprintf (stderr, "[GC:\theap at "FMTPTR" of size %s bytes (+ %s bytes card/cross map),]\n", (uintptr_t)(s->heap.start), uintmaxToCommaString(s->heap.size), uintmaxToCommaString(s->heap.withMapsSize - s->heap.size)); fprintf (stderr, "[GC:\twith old-gen of size %s bytes (%.1f%% of heap),]\n", uintmaxToCommaString(s->heap.oldGenSize), 100.0 * ((double)(s->heap.oldGenSize) / (double)(s->heap.size))); fprintf (stderr, "[GC:\tand nursery of size %s bytes (%.1f%% of heap).]\n", uintmaxToCommaString(nurserySize), 100.0 * ((double)(nurserySize) / (double)(s->heap.size))); } /* Send a GC signal. */ if (s->signalsInfo.gcSignalHandled and s->signalHandlerThread != BOGUS_OBJPTR) { if (DEBUG_SIGNALS) fprintf (stderr, "GC Signal pending.\n"); s->signalsInfo.gcSignalPending = TRUE; unless (s->signalsInfo.amInSignalHandler) s->signalsInfo.signalIsPending = TRUE; } if (DEBUG) displayGCState (s, stderr); assert (hasHeapBytesFree (s, oldGenBytesRequested, nurseryBytesRequested)); assert (invariantForGC (s)); leaveGC (s); }