Bug 1380768 (part 2) - Add a pref for nursery size, r=sfink.
authorPaul Bone <pbone@mozilla.com>
Fri, 21 Jul 2017 14:34:46 +1000
changeset 419559 974fd5c79451c5bd35ceafca95c2ef2563fd3bd1
parent 419558 30c0fb1d099700f028316ee30ce7285134dcd99c
child 419560 3db31de3ac85339bca02e56c11cbe74c566f2340
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1380768
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1380768 (part 2) - Add a pref for nursery size, r=sfink.
dom/base/nsJSEnvironment.cpp
js/public/HeapAPI.h
js/src/gc/GCRuntime.h
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
modules/libpref/init/all.js
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2593,18 +2593,28 @@ SetMemoryHighWaterMarkPrefChangedCallbac
                  highwatermark * 1024L * 1024L);
 }
 
 static void
 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<uint32_t> max = CheckedInt<uint32_t>(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<uint32_t> max = CheckedInt<uint32_t>(pref) * 1024;
+  SetGCParameter(JSGC_MAX_NURSERY_BYTES,
+    max.isValid() ? max.value() : JS::DefaultNurseryBytes);
 }
 
 static void
 SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   bool enableZoneGC = Preferences::GetBool("javascript.options.mem.gc_per_zone");
   bool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental");
   JSGCMode mode;
@@ -2787,16 +2797,18 @@ nsJSContext::EnsureStatics()
   JS::SetAsyncTaskCallbacks(jsapi.cx(), StartAsyncTaskCallback, FinishAsyncTaskCallback);
 
   // Set these global xpconnect options...
   Preferences::RegisterCallbackAndCall(SetMemoryHighWaterMarkPrefChangedCallback,
                                        "javascript.options.mem.high_water_mark");
 
   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");
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
                                        "javascript.options.mem.gc_incremental");
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCSliceTimePrefChangedCallback,
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -96,17 +96,21 @@ AssertGCThingHasType(js::gc::Cell* cell,
 MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell);
 
 } /* namespace gc */
 } /* namespace js */
 
 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(). */
 const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
 
 namespace shadow {
 
 struct Zone
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -135,17 +135,21 @@ class GCSchedulingTunables
     /*
      * Soft limit on the number of bytes we are allowed to allocate in the GC
      * heap. Attempts to allocate gcthings over this limit will return null and
      * subsequently invoke the standard OOM machinery, independent of available
      * physical memory.
      */
     UnprotectedData<size_t> 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<size_t> gcMaxNurseryBytes_;
 
     /*
      * The base value used to compute zone->trigger.gcBytes(). When
      * usage.gcBytes() surpasses threshold.gcBytes() for a zone, the zone may
      * be scheduled for a GC, depending on the exact circumstances.
      */
     ActiveThreadOrGCTaskData<size_t> gcZoneAllocThresholdBase_;
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -641,16 +641,21 @@ js::Nursery::collect(JS::gcreason::Reaso
     }
     endProfile(ProfileKey::Pretenure);
 
     // 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 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_++;
 
     TimeDuration totalTime = profileDurations_[ProfileKey::Total];
     rt->addTelemetry(JS_TELEMETRY_GC_MINOR_US, totalTime.ToMicroseconds());
     rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON, reason);
     if (totalTime.ToMilliseconds() > 1.0)
@@ -919,46 +924,67 @@ js::Nursery::setStartPosition()
     currentStartPosition_ = position();
 }
 
 void
 js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason, double promotionRate)
 {
     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.
     if (gc::IsOOMReason(reason)) {
         minimizeAllocableSpace();
         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;
 }
 
 void
 js::Nursery::growAllocableSpace()
 {
     updateNumChunks(Min(numChunks() * 2, maxNurseryChunks_));
 }
 
 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
 js::Nursery::minimizeAllocableSpace()
 {
 #ifdef JS_GC_ZEAL
     if (runtime()->hasZealMode(ZealMode::GenerationalGC))
         return;
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -463,17 +463,17 @@ class Nursery
      */
     void sweep();
 
     void sweepDictionaryModeObjects();
 
     /* 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. */
     void maybeClearProfileDurations();
     void startProfile(ProfileKey key);
     void endProfile(ProfileKey key);
     static void printProfileDurations(const ProfileDurations& times);
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1431,16 +1431,17 @@ pref("javascript.options.jit.full_debug_
 // fail.
 pref("javascript.options.discardSystemSource", false);
 // This preference limits the memory usage of javascript.
 // If you want to change these values for your device,
 // please find Bug 417052 comment 17 and Bug 456721
 // 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);
 pref("javascript.options.mem.gc_compacting", true);
 pref("javascript.options.mem.log", false);
 pref("javascript.options.mem.notify", false);
 pref("javascript.options.gc_on_memory_pressure", true);
 pref("javascript.options.compact_on_user_inactive", true);