summaryrefslogtreecommitdiffstats
path: root/js/src/gc/GC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/gc/GC.cpp')
-rw-r--r--js/src/gc/GC.cpp138
1 files changed, 85 insertions, 53 deletions
diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp
index c01dfe3660..68dd66898c 100644
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -444,7 +444,9 @@ GCRuntime::GCRuntime(JSRuntime* rt)
#endif
requestSliceAfterBackgroundTask(false),
lifoBlocksToFree((size_t)JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
- lifoBlocksToFreeAfterMinorGC(
+ lifoBlocksToFreeAfterFullMinorGC(
+ (size_t)JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+ lifoBlocksToFreeAfterNextMinorGC(
(size_t)JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
sweepGroupIndex(0),
sweepGroups(nullptr),
@@ -658,7 +660,7 @@ void GCRuntime::setZeal(uint8_t zeal, uint32_t frequency) {
if (zeal == 0) {
if (hasZealMode(ZealMode::GenerationalGC)) {
- evictNursery(JS::GCReason::DEBUG_GC);
+ evictNursery();
nursery().leaveZealMode();
}
@@ -669,7 +671,7 @@ void GCRuntime::setZeal(uint8_t zeal, uint32_t frequency) {
ZealMode zealMode = ZealMode(zeal);
if (zealMode == ZealMode::GenerationalGC) {
- evictNursery(JS::GCReason::DEBUG_GC);
+ evictNursery(JS::GCReason::EVICT_NURSERY);
nursery().enterZealMode();
}
@@ -704,7 +706,7 @@ void GCRuntime::unsetZeal(uint8_t zeal) {
}
if (zealMode == ZealMode::GenerationalGC) {
- evictNursery(JS::GCReason::DEBUG_GC);
+ evictNursery();
nursery().leaveZealMode();
}
@@ -1073,6 +1075,11 @@ bool GCRuntime::setParameter(JSGCParamKey key, uint32_t value,
marker->incrementalWeakMapMarkingEnabled = value != 0;
}
break;
+ case JSGC_SEMISPACE_NURSERY_ENABLED: {
+ AutoUnlockGC unlock(lock);
+ nursery().setSemispaceEnabled(value);
+ break;
+ }
case JSGC_MIN_EMPTY_CHUNK_COUNT:
setMinEmptyChunkCount(value, lock);
break;
@@ -1160,6 +1167,11 @@ void GCRuntime::resetParameter(JSGCParamKey key, AutoLockGC& lock) {
TuningDefaults::IncrementalWeakMapMarkingEnabled;
}
break;
+ case JSGC_SEMISPACE_NURSERY_ENABLED: {
+ AutoUnlockGC unlock(lock);
+ nursery().setSemispaceEnabled(TuningDefaults::SemispaceNurseryEnabled);
+ break;
+ }
case JSGC_MIN_EMPTY_CHUNK_COUNT:
setMinEmptyChunkCount(TuningDefaults::MinEmptyChunkCount, lock);
break;
@@ -1241,6 +1253,8 @@ uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) {
return parallelMarkingEnabled;
case JSGC_INCREMENTAL_WEAKMAP_ENABLED:
return marker().incrementalWeakMapMarkingEnabled;
+ case JSGC_SEMISPACE_NURSERY_ENABLED:
+ return nursery().semispaceEnabled();
case JSGC_CHUNK_BYTES:
return ChunkSize;
case JSGC_HELPER_THREAD_RATIO:
@@ -2135,7 +2149,7 @@ void GCRuntime::queueUnusedLifoBlocksForFree(LifoAlloc* lifo) {
}
void GCRuntime::queueAllLifoBlocksForFreeAfterMinorGC(LifoAlloc* lifo) {
- lifoBlocksToFreeAfterMinorGC.ref().transferFrom(lifo);
+ lifoBlocksToFreeAfterFullMinorGC.ref().transferFrom(lifo);
}
void GCRuntime::queueBuffersForFreeAfterMinorGC(Nursery::BufferSet& buffers) {
@@ -2741,24 +2755,7 @@ void GCRuntime::endPreparePhase(JS::GCReason reason) {
MOZ_ASSERT(unmarkTask.isIdle());
for (GCZonesIter zone(this); !zone.done(); zone.next()) {
- /*
- * In an incremental GC, clear the area free lists to ensure that subsequent
- * allocations refill them and end up marking new cells back. See
- * arenaAllocatedDuringGC().
- */
- zone->arenas.clearFreeLists();
-
zone->setPreservingCode(false);
-
-#ifdef JS_GC_ZEAL
- if (hasZealMode(ZealMode::YieldBeforeRootMarking)) {
- for (auto kind : AllAllocKinds()) {
- for (ArenaIterInGC arena(zone, kind); !arena.done(); arena.next()) {
- arena->checkNoMarkedCells();
- }
- }
- }
-#endif
}
// Discard JIT code more aggressively if the process is approaching its
@@ -2800,7 +2797,7 @@ void GCRuntime::endPreparePhase(JS::GCReason reason) {
*/
{
- gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::PREPARE);
+ gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PREPARE);
AutoLockHelperThreadState helperLock;
@@ -2817,20 +2814,6 @@ void GCRuntime::endPreparePhase(JS::GCReason reason) {
haveDiscardedJITCodeThisSlice = true;
/*
- * Relazify functions after discarding JIT code (we can't relazify
- * functions with JIT code) and before the actual mark phase, so that
- * the current GC can collect the JSScripts we're unlinking here. We do
- * this only when we're performing a shrinking GC, as too much
- * relazification can cause performance issues when we have to reparse
- * the same functions over and over.
- */
- if (isShrinkingGC()) {
- relazifyFunctionsForShrinkingGC();
- purgePropMapTablesForShrinkingGC();
- purgeSourceURLsForShrinkingGC();
- }
-
- /*
* We must purge the runtime at the beginning of an incremental GC. The
* danger if we purge later is that the snapshot invariant of
* incremental GC will be broken, as follows. If some object is
@@ -2840,8 +2823,25 @@ void GCRuntime::endPreparePhase(JS::GCReason reason) {
* it. This object might never be marked, so a GC hazard would exist.
*/
purgeRuntime();
+ }
+
+ // This will start background free for lifo blocks queued by purgeRuntime,
+ // even if there's nothing in the nursery.
+ collectNurseryFromMajorGC(reason);
- startBackgroundFreeAfterMinorGC();
+ {
+ gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PREPARE);
+ // Relazify functions after discarding JIT code (we can't relazify functions
+ // with JIT code) and before the actual mark phase, so that the current GC
+ // can collect the JSScripts we're unlinking here. We do this only when
+ // we're performing a shrinking GC, as too much relazification can cause
+ // performance issues when we have to reparse the same functions over and
+ // over.
+ if (isShrinkingGC()) {
+ relazifyFunctionsForShrinkingGC();
+ purgePropMapTablesForShrinkingGC();
+ purgeSourceURLsForShrinkingGC();
+ }
if (isShutdownGC()) {
/* Clear any engine roots that may hold external data live. */
@@ -2903,6 +2903,21 @@ void GCRuntime::beginMarkPhase(AutoGCSession& session) {
#endif
for (GCZonesIter zone(this); !zone.done(); zone.next()) {
+ // In an incremental GC, clear the arena free lists to ensure that
+ // subsequent allocations refill them and end up marking new cells black.
+ // See arenaAllocatedDuringGC().
+ zone->arenas.clearFreeLists();
+
+#ifdef JS_GC_ZEAL
+ if (hasZealMode(ZealMode::YieldBeforeRootMarking)) {
+ for (auto kind : AllAllocKinds()) {
+ for (ArenaIter arena(zone, kind); !arena.done(); arena.next()) {
+ arena->checkNoMarkedCells();
+ }
+ }
+ }
+#endif
+
// Incremental marking barriers are enabled at this point.
zone->changeGCState(Zone::Prepare, zone->initialMarkingState());
@@ -3715,11 +3730,8 @@ void GCRuntime::incrementalSlice(SliceBudget& budget, JS::GCReason reason,
[[fallthrough]];
case State::MarkRoots:
- if (NeedToCollectNursery(this)) {
- collectNurseryFromMajorGC(reason);
- }
-
endPreparePhase(reason);
+
beginMarkPhase(session);
incrementalState = State::Mark;
@@ -3878,8 +3890,11 @@ void GCRuntime::incrementalSlice(SliceBudget& budget, JS::GCReason reason,
}
void GCRuntime::collectNurseryFromMajorGC(JS::GCReason reason) {
- collectNursery(gcOptions(), reason,
+ collectNursery(gcOptions(), JS::GCReason::EVICT_NURSERY,
gcstats::PhaseKind::EVICT_NURSERY_FOR_MAJOR_GC);
+
+ MOZ_ASSERT(nursery().isEmpty());
+ MOZ_ASSERT(storeBuffer().isEmpty());
}
bool GCRuntime::hasForegroundWork() const {
@@ -4733,26 +4748,43 @@ void GCRuntime::collectNursery(JS::GCOptions options, JS::GCReason reason,
gcstats::AutoPhase ap(stats(), phase);
nursery().collect(options, reason);
- MOZ_ASSERT(nursery().isEmpty());
startBackgroundFreeAfterMinorGC();
+
+ // We ignore gcMaxBytes when allocating for minor collection. However, if we
+ // overflowed, we disable the nursery. The next time we allocate, we'll fail
+ // because bytes >= gcMaxBytes.
+ if (heapSize.bytes() >= tunables.gcMaxBytes()) {
+ if (!nursery().isEmpty()) {
+ nursery().collect(options, JS::GCReason::DISABLE_GENERATIONAL_GC);
+ MOZ_ASSERT(nursery().isEmpty());
+ startBackgroundFreeAfterMinorGC();
+ }
+ nursery().disable();
+ }
}
void GCRuntime::startBackgroundFreeAfterMinorGC() {
- MOZ_ASSERT(nursery().isEmpty());
+ // Called after nursery collection. Free whatever blocks are safe to free now.
- {
- AutoLockHelperThreadState lock;
+ AutoLockHelperThreadState lock;
- lifoBlocksToFree.ref().transferFrom(&lifoBlocksToFreeAfterMinorGC.ref());
+ lifoBlocksToFree.ref().transferFrom(&lifoBlocksToFreeAfterNextMinorGC.ref());
- if (lifoBlocksToFree.ref().isEmpty() &&
- buffersToFreeAfterMinorGC.ref().empty()) {
- return;
- }
+ if (nursery().tenuredEverything) {
+ lifoBlocksToFree.ref().transferFrom(
+ &lifoBlocksToFreeAfterFullMinorGC.ref());
+ } else {
+ lifoBlocksToFreeAfterNextMinorGC.ref().transferFrom(
+ &lifoBlocksToFreeAfterFullMinorGC.ref());
+ }
+
+ if (lifoBlocksToFree.ref().isEmpty() &&
+ buffersToFreeAfterMinorGC.ref().empty()) {
+ return;
}
- startBackgroundFree();
+ freeTask.startOrRunIfIdle(lock);
}
bool GCRuntime::gcIfRequestedImpl(bool eagerOk) {