diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index c9e2da1a4eb0..0c8b0f6905ef 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2598,8 +2598,18 @@ SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure) { int32_t pref = Preferences::GetInt(aPrefName, -1); // handle overflow and negative pref values - uint32_t max = (pref <= 0 || pref >= 0x1000) ? -1 : (uint32_t)pref * 1024 * 1024; - SetGCParameter(JSGC_MAX_BYTES, max); + CheckedInt max = CheckedInt(pref) * 1024 * 1024; + SetGCParameter(JSGC_MAX_BYTES, max.isValid() ? max.value() : -1); +} + +static void +SetMemoryNurseryMaxPrefChangedCallback(const char* aPrefName, void* aClosure) +{ + int32_t pref = Preferences::GetInt(aPrefName, -1); + // handle overflow and negative pref values + CheckedInt max = CheckedInt(pref) * 1024; + SetGCParameter(JSGC_MAX_NURSERY_BYTES, + max.isValid() ? max.value() : JS::DefaultNurseryBytes); } static void @@ -2792,6 +2802,8 @@ nsJSContext::EnsureStatics() Preferences::RegisterCallbackAndCall(SetMemoryMaxPrefChangedCallback, "javascript.options.mem.max"); + Preferences::RegisterCallbackAndCall(SetMemoryNurseryMaxPrefChangedCallback, + "javascript.options.mem.nursery.max_kb"); Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback, "javascript.options.mem.gc_per_zone"); diff --git a/js/public/HeapAPI.h b/js/public/HeapAPI.h index 3fb92ffaae81..34a3e156e66d 100644 --- a/js/public/HeapAPI.h +++ b/js/public/HeapAPI.h @@ -101,7 +101,11 @@ MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell); namespace JS { struct Zone; -/* Default size for the generational nursery in bytes. */ +/* + * Default size for the generational nursery in bytes. + * This is the initial nursery size, when running in the browser this is + * updated by JS_SetGCParameter(). + */ const uint32_t DefaultNurseryBytes = 16 * js::gc::ChunkSize; /* Default maximum heap size in bytes to pass to JS_NewContext(). */ diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index c2ff7fe72bb9..984ff9064101 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -140,7 +140,11 @@ class GCSchedulingTunables */ UnprotectedData gcMaxBytes_; - /* Maximum nursery size for each zone group. */ + /* + * Maximum nursery size for each zone group. + * Initially DefaultNurseryBytes and can be set by + * javascript.options.mem.nursery.max_kb + */ ActiveThreadData gcMaxNurseryBytes_; /* diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 624945163369..9fea421cc170 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -646,6 +646,11 @@ js::Nursery::collect(JS::gcreason::Reason reason) // because gcBytes >= gcMaxBytes. if (rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes()) disable(); + // Disable the nursery if the user changed the configuration setting. The + // nursery can only be re-enabled by resetting the configurationa and + // restarting firefox. + if (maxNurseryChunks_ == 0) + disable(); endProfile(ProfileKey::Total); minorGcCount_++; @@ -924,6 +929,7 @@ js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason, double promotionRat { static const double GrowThreshold = 0.05; static const double ShrinkThreshold = 0.01; + unsigned newMaxNurseryChunks; // Shrink the nursery to its minimum size of we ran out of memory or // received a memory pressure event. @@ -932,10 +938,30 @@ js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason, double promotionRat return; } +#ifdef JS_GC_ZEAL + // This zeal mode disabled nursery resizing. + if (runtime()->hasZealMode(ZealMode::GenerationalGC)) + return; +#endif + + newMaxNurseryChunks = runtime()->gc.tunables.gcMaxNurseryBytes() >> ChunkShift; + if (newMaxNurseryChunks != maxNurseryChunks_) { + maxNurseryChunks_ = newMaxNurseryChunks; + /* The configured maximum nursery size is changing */ + int extraChunks = numChunks() - newMaxNurseryChunks; + if (extraChunks > 0) { + /* We need to shrink the nursery */ + shrinkAllocableSpace(extraChunks); + + previousPromotionRate_ = promotionRate; + return; + } + } + if (promotionRate > GrowThreshold) growAllocableSpace(); else if (promotionRate < ShrinkThreshold && previousPromotionRate_ < ShrinkThreshold) - shrinkAllocableSpace(); + shrinkAllocableSpace(1); previousPromotionRate_ = promotionRate; } @@ -947,13 +973,13 @@ js::Nursery::growAllocableSpace() } void -js::Nursery::shrinkAllocableSpace() +js::Nursery::shrinkAllocableSpace(unsigned removeNumChunks) { #ifdef JS_GC_ZEAL if (runtime()->hasZealMode(ZealMode::GenerationalGC)) return; #endif - updateNumChunks(Max(numChunks() - 1, 1u)); + updateNumChunks(Max(numChunks() - removeNumChunks, 1u)); } void diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 78622a0fdc88..cd87d51c7da3 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -468,7 +468,7 @@ class Nursery /* Change the allocable space provided by the nursery. */ void maybeResizeNursery(JS::gcreason::Reason reason, double promotionRate); void growAllocableSpace(); - void shrinkAllocableSpace(); + void shrinkAllocableSpace(unsigned removeNumChunks); void minimizeAllocableSpace(); /* Profile recording and printing. */ diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index a33da49fe497..d2c71cf15d6e 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1436,6 +1436,7 @@ pref("javascript.options.discardSystemSource", false); // Comment 32 and Bug 613551. pref("javascript.options.mem.high_water_mark", 128); pref("javascript.options.mem.max", -1); +pref("javascript.options.mem.nursery.max_kb", -1); pref("javascript.options.mem.gc_per_zone", true); pref("javascript.options.mem.gc_incremental", true); pref("javascript.options.mem.gc_incremental_slice_ms", 5);