diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 4bded5b5c09a..4a8dee8fc4ea 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -274,7 +274,8 @@ class GCRuntime bool triggerGC(JS::gcreason::Reason reason); bool triggerZoneGC(Zone *zone, JS::gcreason::Reason reason); - void maybeGC(Zone *zone); + bool maybeGC(Zone *zone); + void maybePeriodicFullGC(); void minorGC(JS::gcreason::Reason reason); void minorGC(JSContext *cx, JS::gcreason::Reason reason); void gcIfNeeded(JSContext *cx); @@ -487,7 +488,7 @@ class GCRuntime void getNextZoneGroup(); void endMarkingZoneGroup(); void beginSweepingZoneGroup(); - bool releaseObservedTypes(); + bool shouldReleaseObservedTypes(); void endSweepingZoneGroup(); bool sweepPhase(SliceBudget &sliceBudget); void endSweepPhase(JSGCInvocationKind gckind, bool lastGC); @@ -567,7 +568,6 @@ class GCRuntime bool chunkAllocationSinceLastGC; int64_t nextFullGCTime; int64_t lastGCTime; - int64_t jitReleaseTime; JSGCMode mode; @@ -589,6 +589,12 @@ class GCRuntime */ volatile uintptr_t isNeeded; + /* Incremented at the start of every major GC. */ + uint64_t majorGCNumber; + + /* The major GC number at which to release observed type information. */ + uint64_t jitReleaseNumber; + /* Incremented on every GC slice. */ uint64_t number; @@ -624,6 +630,9 @@ class GCRuntime /* Whether any sweeping will take place in the separate GC helper thread. */ bool sweepOnBackgroundThread; + /* Whether observed type information is being released in the current GC. */ + bool releaseObservedTypes; + /* Whether any black->gray edges were found during marking. */ bool foundBlackGrayEdges; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 4361ee3d86da..c2d15765045b 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1879,7 +1879,9 @@ JS_GC(JSRuntime *rt) JS_PUBLIC_API(void) JS_MaybeGC(JSContext *cx) { - MaybeGC(cx); + GCRuntime &gc = cx->runtime()->gc; + if (!gc.maybeGC(cx->zone())) + gc.maybePeriodicFullGC(); } JS_PUBLIC_API(void) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index f85a1ee7d9c2..2d7f2dbdcba5 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1112,12 +1112,13 @@ GCRuntime::GCRuntime(JSRuntime *rt) : chunkAllocationSinceLastGC(false), nextFullGCTime(0), lastGCTime(0), - jitReleaseTime(0), mode(JSGC_MODE_INCREMENTAL), decommitThreshold(32 * 1024 * 1024), cleanUpEverything(false), grayBitsValid(false), isNeeded(0), + majorGCNumber(0), + jitReleaseNumber(0), number(0), startNumber(0), isFull(false), @@ -1248,8 +1249,11 @@ GCRuntime::initZeal() #endif -/* Lifetime for type sets attached to scripts containing observed types. */ -static const int64_t JIT_SCRIPT_RELEASE_TYPES_INTERVAL = 60 * 1000 * 1000; +/* + * Lifetime in number of major GCs for type sets attached to scripts containing + * observed types. + */ +static const uint64_t JIT_SCRIPT_RELEASE_TYPES_PERIOD = 20; bool GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) @@ -1276,9 +1280,7 @@ GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) tunables.setParameter(JSGC_MAX_BYTES, maxbytes); setMaxMallocBytes(maxbytes); -#ifndef JS_MORE_DETERMINISTIC - jitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL; -#endif + jitReleaseNumber = majorGCNumber + JIT_SCRIPT_RELEASE_TYPES_PERIOD; #ifdef JSGC_GENERATIONAL if (!nursery.init(maxNurseryBytes)) @@ -2415,13 +2417,7 @@ GCRuntime::triggerZoneGC(Zone *zone, JS::gcreason::Reason reason) return true; } -void -js::MaybeGC(JSContext *cx) -{ - cx->runtime()->gc.maybeGC(cx->zone()); -} - -void +bool GCRuntime::maybeGC(Zone *zone) { JS_ASSERT(CurrentThreadCanAccessRuntime(rt)); @@ -2430,13 +2426,13 @@ GCRuntime::maybeGC(Zone *zone) if (zealMode == ZealAllocValue || zealMode == ZealPokeValue) { JS::PrepareForFullGC(rt); GC(rt, GC_NORMAL, JS::gcreason::MAYBEGC); - return; + return true; } #endif if (isNeeded) { GCSlice(rt, GC_NORMAL, JS::gcreason::MAYBEGC); - return; + return true; } double factor = schedulingState.inHighFrequencyGCMode() ? 0.85 : 0.9; @@ -2447,15 +2443,25 @@ GCRuntime::maybeGC(Zone *zone) { PrepareZoneForGC(zone); GCSlice(rt, GC_NORMAL, JS::gcreason::MAYBEGC); - return; + return true; } -#ifndef JS_MORE_DETERMINISTIC + return false; +} + +void +GCRuntime::maybePeriodicFullGC() +{ /* + * Trigger a periodic full GC. + * + * This is a source of non-determinism, but is not called from the shell. + * * Access to the counters and, on 32 bit, setting gcNextFullGCTime below * is not atomic and a race condition could trigger or suppress the GC. We * tolerate this. */ +#ifndef JS_MORE_DETERMINISTIC int64_t now = PRMJ_Now(); if (nextFullGCTime && nextFullGCTime <= now) { if (chunkAllocationSinceLastGC || @@ -2881,7 +2887,7 @@ GCHelperState::onBackgroundThread() } bool -GCRuntime::releaseObservedTypes() +GCRuntime::shouldReleaseObservedTypes() { bool releaseTypes = false; @@ -2890,13 +2896,12 @@ GCRuntime::releaseObservedTypes() releaseTypes = true; #endif -#ifndef JS_MORE_DETERMINISTIC - int64_t now = PRMJ_Now(); - if (now >= jitReleaseTime) + MOZ_ASSERT(majorGCNumber <= jitReleaseNumber); + if (majorGCNumber == jitReleaseNumber) releaseTypes = true; + if (releaseTypes) - jitReleaseTime = now + JIT_SCRIPT_RELEASE_TYPES_INTERVAL; -#endif + jitReleaseNumber = majorGCNumber + JIT_SCRIPT_RELEASE_TYPES_PERIOD; return releaseTypes; } @@ -4157,10 +4162,9 @@ GCRuntime::beginSweepingZoneGroup() zone->discardJitCode(&fop); } - bool releaseTypes = releaseObservedTypes(); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { gcstats::AutoSCC scc(stats, zoneGroupIndex); - c->sweep(&fop, releaseTypes && !c->zone()->isPreservingCode()); + c->sweep(&fop, releaseObservedTypes && !c->zone()->isPreservingCode()); } for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { @@ -4174,7 +4178,7 @@ GCRuntime::beginSweepingZoneGroup() // code and new script information in the zone, the only things // whose correctness depends on the type constraints. bool oom = false; - zone->sweep(&fop, releaseTypes && !zone->isPreservingCode(), &oom); + zone->sweep(&fop, releaseObservedTypes && !zone->isPreservingCode(), &oom); if (oom) { zone->setPreservingCode(false); @@ -4264,6 +4268,8 @@ GCRuntime::beginSweepPhase(bool lastGC) sweepOnBackgroundThread = !lastGC && !TraceEnabled() && CanUseExtraThreads(); + releaseObservedTypes = shouldReleaseObservedTypes(); + #ifdef DEBUG for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { JS_ASSERT(!c->gcIncomingGrayPointers); @@ -4986,6 +4992,8 @@ GCRuntime::gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind, interFrameGC = true; number++; + if (incrementalState == NO_INCREMENTAL) + majorGCNumber++; // It's ok if threads other than the main thread have suppressGC set, as // they are operating on zones which will not be collected from here. diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 3f3f4265b88b..71ae41121c7e 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -971,9 +971,6 @@ TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason); extern bool TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason); -extern void -MaybeGC(JSContext *cx); - extern void ReleaseAllJITCode(FreeOp *op); diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 2ae37ea6aba7..8e764d08d0d6 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -691,7 +691,7 @@ AllocateObjectForCacheHit(JSContext *cx, AllocKind kind, InitialHeap heap) JSObject *obj = AllocateObject(cx, kind, 0, heap); if (!obj && allowGC) { - MaybeGC(cx); + cx->runtime()->gc.maybeGC(cx->zone()); return nullptr; }