Bug 1251463 - Remove ShrinkGCBuffers; r=jonco,r=mccr8
authorTerrence Cole <terrence@mozilla.com>
Thu, 25 Feb 2016 15:52:59 -0800
changeset 306204 f35cc482a71b0692778d0961b148c4e43aee277e
parent 306203 02016837381082d203059b4cc193f5f4ced3ef50
child 306205 c8a8de530e654f7cd0f83d0057bdb19b83cf8c24
push id30480
push usercbook@mozilla.com
push dateFri, 22 Jul 2016 09:58:20 +0000
treeherdermozilla-central@e0bc88708ffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, mccr8
bugs1251463
milestone50.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 1251463 - Remove ShrinkGCBuffers; r=jonco,r=mccr8
dom/base/nsJSEnvironment.cpp
dom/base/nsJSEnvironment.h
js/public/GCAPI.h
js/src/gc/GCRuntime.h
js/src/gc/Heap.h
js/src/jsgc.cpp
js/src/jsgc.h
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -136,17 +136,16 @@ static const uint32_t kMaxICCDuration = 
 #define NS_CC_PURPLE_LIMIT          200
 
 // Large value used to specify that a script should run essentially forever
 #define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
 
 // if you add statics here, add them to the list in StartupJSEnvironment
 
 static nsITimer *sGCTimer;
-static nsITimer *sShrinkGCBuffersTimer;
 static nsITimer *sShrinkingGCTimer;
 static nsITimer *sCCTimer;
 static nsITimer *sICCTimer;
 static nsITimer *sFullGCTimer;
 static nsITimer *sInterSliceGCTimer;
 
 static TimeStamp sLastCCEndTime;
 
@@ -290,17 +289,16 @@ GetCollectionTimeDelta()
   return 0;
 }
 
 static void
 KillTimers()
 {
   nsJSContext::KillGCTimer();
   nsJSContext::KillShrinkingGCTimer();
-  nsJSContext::KillShrinkGCBuffersTimer();
   nsJSContext::KillCCTimer();
   nsJSContext::KillICCTimer();
   nsJSContext::KillFullGCTimer();
   nsJSContext::KillInterSliceGCTimer();
 }
 
 // If we collected a substantial amount of cycles, poke the GC since more objects
 // might be unreachable now.
@@ -1183,17 +1181,16 @@ nsJSContext::GarbageCollectNow(JS::gcrea
                                int64_t aSliceMillis)
 {
   PROFILER_LABEL("nsJSContext", "GarbageCollectNow",
     js::ProfileEntry::Category::GC);
 
   MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC);
 
   KillGCTimer();
-  KillShrinkGCBuffersTimer();
 
   // Reset sPendingLoadCount in case the timer that fired was a
   // timer we scheduled due to a normal GC timer firing while
   // documents were loading. If this happens we're waiting for a
   // document that is taking a long time to load, and we effectively
   // ignore the fact that the currently loading documents are still
   // loading and move on as if they weren't.
   sPendingLoadCount = 0;
@@ -1221,28 +1218,16 @@ nsJSContext::GarbageCollectNow(JS::gcrea
 
   if (aIncremental == IncrementalGC) {
     JS::StartIncrementalGC(sContext, gckind, aReason, aSliceMillis);
   } else {
     JS::GCForReason(sContext, gckind, aReason);
   }
 }
 
-//static
-void
-nsJSContext::ShrinkGCBuffersNow()
-{
-  PROFILER_LABEL("nsJSContext", "ShrinkGCBuffersNow",
-    js::ProfileEntry::Category::GC);
-
-  KillShrinkGCBuffersTimer();
-
-  JS::ShrinkGCBuffers(sContext);
-}
-
 static void
 FinishAnyIncrementalGC()
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GC);
 
   if (sCCLockedOut) {
     // We're in the middle of an incremental GC, so finish it.
     JS::PrepareForIncrementalGC(sContext);
@@ -1726,23 +1711,16 @@ void
 GCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   nsJSContext::KillGCTimer();
   uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
   nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
                                  nsJSContext::IncrementalGC);
 }
 
-void
-ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
-{
-  nsJSContext::KillShrinkGCBuffersTimer();
-  nsJSContext::ShrinkGCBuffersNow();
-}
-
 // static
 void
 ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure)
 {
   nsJSContext::KillShrinkingGCTimer();
   sIsCompactingOnUserInactive = true;
   nsJSContext::GarbageCollectNow(JS::gcreason::USER_INACTIVE,
                                  nsJSContext::IncrementalGC,
@@ -1961,38 +1939,16 @@ nsJSContext::PokeGC(JS::gcreason::Reason
                                          : NS_GC_DELAY),
                                       nsITimer::TYPE_ONE_SHOT,
                                       "GCTimerFired");
   first = false;
 }
 
 // static
 void
-nsJSContext::PokeShrinkGCBuffers()
-{
-  if (sShrinkGCBuffersTimer || sShuttingDown) {
-    return;
-  }
-
-  CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer);
-
-  if (!sShrinkGCBuffersTimer) {
-    // Failed to create timer (probably because we're in XPCOM shutdown)
-    return;
-  }
-
-  sShrinkGCBuffersTimer->InitWithNamedFuncCallback(ShrinkGCBuffersTimerFired,
-                                                   nullptr,
-                                                   NS_SHRINK_GC_BUFFERS_DELAY,
-                                                   nsITimer::TYPE_ONE_SHOT,
-                                                   "ShrinkGCBuffersTimerFired");
-}
-
-// static
-void
 nsJSContext::PokeShrinkingGC()
 {
   if (sShrinkingGCTimer || sShuttingDown) {
     return;
   }
 
   CallCreateInstance("@mozilla.org/timer;1", &sShrinkingGCTimer);
 
@@ -2056,26 +2012,16 @@ nsJSContext::KillInterSliceGCTimer()
   if (sInterSliceGCTimer) {
     sInterSliceGCTimer->Cancel();
     NS_RELEASE(sInterSliceGCTimer);
   }
 }
 
 //static
 void
-nsJSContext::KillShrinkGCBuffersTimer()
-{
-  if (sShrinkGCBuffersTimer) {
-    sShrinkGCBuffersTimer->Cancel();
-    NS_RELEASE(sShrinkGCBuffersTimer);
-  }
-}
-
-//static
-void
 nsJSContext::KillShrinkingGCTimer()
 {
   if (sShrinkingGCTimer) {
     sShrinkingGCTimer->Cancel();
     NS_RELEASE(sShrinkingGCTimer);
   }
 }
 
@@ -2133,19 +2079,16 @@ static void
 DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescription &aDesc)
 {
   NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
 
   switch (aProgress) {
     case JS::GC_CYCLE_BEGIN: {
       // Prevent cycle collections and shrinking during incremental GC.
       sCCLockedOut = true;
-
-      nsJSContext::KillShrinkGCBuffersTimer();
-
       break;
     }
 
     case JS::GC_CYCLE_END: {
       PRTime delta = GetCollectionTimeDelta();
 
       if (sPostGCEventsToConsole) {
         NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f)[%s] ");
@@ -2190,20 +2133,16 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::G
                                                   NS_FULL_GC_DELAY,
                                                   nsITimer::TYPE_ONE_SHOT,
                                                   "FullGCTimerFired");
         }
       } else {
         nsJSContext::KillFullGCTimer();
       }
 
-      if (aDesc.invocationKind_ == GC_NORMAL) {
-        nsJSContext::PokeShrinkGCBuffers();
-      }
-
       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
         nsCycleCollector_dispatchDeferredDeletion();
       }
 
       break;
     }
 
     case JS::GC_SLICE_BEGIN:
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -82,17 +82,16 @@ public:
 
   // Setup all the statics etc - safe to call multiple times after Startup().
   void EnsureStatics();
 
   static void GarbageCollectNow(JS::gcreason::Reason reason,
                                 IsIncremental aIncremental = NonIncrementalGC,
                                 IsShrinking aShrinking = NonShrinkingGC,
                                 int64_t aSliceMillis = 0);
-  static void ShrinkGCBuffersNow();
 
   // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be
   // called even if the previous collection was GC.
   static void CycleCollectNow(nsICycleCollectorListener *aListener = nullptr,
                               int32_t aExtraForgetSkippableCalls = 0);
 
   // Run a cycle collector slice, using a heuristic to decide how long to run it.
   static void RunCycleCollectorSlice();
@@ -107,19 +106,16 @@ public:
   static uint32_t GetMaxCCSliceTimeSinceClear();
   static void ClearMaxCCSliceTime();
 
   static void RunNextCollectorTimer();
 
   static void PokeGC(JS::gcreason::Reason aReason, int aDelay = 0);
   static void KillGCTimer();
 
-  static void PokeShrinkGCBuffers();
-  static void KillShrinkGCBuffersTimer();
-
   static void PokeShrinkingGC();
   static void KillShrinkingGCTimer();
 
   static void MaybePokeCC();
   static void KillCCTimer();
   static void KillICCTimer();
   static void KillFullGCTimer();
   static void KillInterSliceGCTimer();
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -469,24 +469,16 @@ IsGenerationalGCEnabled(JSRuntime* rt);
  * Returns the GC's "number". This does not correspond directly to the number
  * of GCs that have been run, but is guaranteed to be monotonically increasing
  * with GC activity.
  */
 extern JS_PUBLIC_API(size_t)
 GetGCNumber();
 
 /**
- * The GC does not immediately return the unused memory freed by a collection
- * back to the system incase it is needed soon afterwards. This call forces the
- * GC to return this memory immediately.
- */
-extern JS_PUBLIC_API(void)
-ShrinkGCBuffers(JSContext* cx);
-
-/**
  * Assert if a GC occurs while this class is live. This class does not disable
  * the static rooting hazard analysis.
  */
 class JS_PUBLIC_API(AutoAssertOnGC)
 {
 #ifdef DEBUG
     js::gc::GCRuntime* gc;
     size_t gcNumber;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -921,17 +921,18 @@ class GCRuntime
     static TenuredCell* refillFreeListFromMainThread(JSContext* cx, AllocKind thingKind,
                                                      size_t thingSize);
     static TenuredCell* refillFreeListOffMainThread(ExclusiveContext* cx, AllocKind thingKind);
 
     /*
      * Return the list of chunks that can be released outside the GC lock.
      * Must be called either during the GC or with the GC lock taken.
      */
-    ChunkPool expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC& lock);
+    friend class BackgroundDecommitTask;
+    ChunkPool expireEmptyChunkPool(const AutoLockGC& lock);
     void freeEmptyChunks(JSRuntime* rt, const AutoLockGC& lock);
     void prepareToFreeChunk(ChunkInfo& info);
 
     friend class BackgroundAllocTask;
     friend class AutoMaybeStartBackgroundAllocation;
     bool wantBackgroundAllocation(const AutoLockGC& lock) const;
     void startBackgroundAllocTaskIfIdle();
 
@@ -979,17 +980,16 @@ class GCRuntime
     void beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock);
     bool shouldReleaseObservedTypes();
     void endSweepingZoneGroup();
     IncrementalProgress sweepPhase(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock);
     void endSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
     void sweepZones(FreeOp* fop, bool lastGC);
     void decommitAllWithoutUnlocking(const AutoLockGC& lock);
     void startDecommit();
-    void expireChunksAndArenas(bool shouldShrink, AutoLockGC& lock);
     void queueZonesForBackgroundSweep(ZoneList& zones);
     void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks, ThreadType threadType);
     void assertBackgroundSweepingFinished();
     bool shouldCompact();
     void beginCompactPhase();
     IncrementalProgress compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
                                      AutoLockForExclusiveAccess& lock);
     void endCompactPhase(JS::gcreason::Reason reason);
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -779,52 +779,48 @@ struct ChunkTrailer
 static_assert(sizeof(ChunkTrailer) == ChunkTrailerSize,
               "ChunkTrailer size must match the API defined size.");
 
 /* The chunk header (located at the end of the chunk to preserve arena alignment). */
 struct ChunkInfo
 {
     void init() {
         next = prev = nullptr;
-        age = 0;
     }
 
   private:
     friend class ChunkPool;
     Chunk*          next;
     Chunk*          prev;
 
   public:
     /* Free arenas are linked together with arena.next. */
     Arena*          freeArenasHead;
 
 #if JS_BITS_PER_WORD == 32
     /*
      * Calculating sizes and offsets is simpler if sizeof(ChunkInfo) is
      * architecture-independent.
      */
-    char            padding[20];
+    char            padding[24];
 #endif
 
     /*
      * Decommitted arenas are tracked by a bitmap in the chunk header. We use
      * this offset to start our search iteration close to a decommitted arena
      * that we can allocate.
      */
     uint32_t        lastDecommittedArenaOffset;
 
     /* Number of free arenas, either committed or decommitted. */
     uint32_t        numArenasFree;
 
     /* Number of free, committed arenas. */
     uint32_t        numArenasFreeCommitted;
 
-    /* Number of GC cycles this chunk has survived. */
-    uint32_t        age;
-
     /* Information shared by all Chunk types. */
     ChunkTrailer    trailer;
 };
 
 /*
  * Calculating ArenasPerChunk:
  *
  * In order to figure out how many Arenas will fit in a chunk, we need to know
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -554,17 +554,16 @@ ChunkPool::pop()
 }
 
 void
 ChunkPool::push(Chunk* chunk)
 {
     MOZ_ASSERT(!chunk->info.next);
     MOZ_ASSERT(!chunk->info.prev);
 
-    chunk->info.age = 0;
     chunk->info.next = head_;
     if (head_)
         head_->info.prev = chunk;
     head_ = chunk;
     ++count_;
 
     MOZ_ASSERT(verify());
 }
@@ -617,51 +616,32 @@ ChunkPool::verify() const
 void
 ChunkPool::Iter::next()
 {
     MOZ_ASSERT(!done());
     current_ = current_->info.next;
 }
 
 ChunkPool
-GCRuntime::expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC& lock)
-{
-    /*
-     * Return old empty chunks to the system while preserving the order of
-     * other chunks in the list. This way, if the GC runs several times
-     * without emptying the list, the older chunks will stay at the tail
-     * and are more likely to reach the max age.
-     */
+GCRuntime::expireEmptyChunkPool(const AutoLockGC& lock)
+{
     MOZ_ASSERT(emptyChunks(lock).verify());
+    MOZ_ASSERT(tunables.minEmptyChunkCount(lock) <= tunables.maxEmptyChunkCount());
+
     ChunkPool expired;
-    unsigned freeChunkCount = 0;
-    for (ChunkPool::Iter iter(emptyChunks(lock)); !iter.done();) {
-        Chunk* chunk = iter.get();
-        iter.next();
-
-        MOZ_ASSERT(chunk->unused());
-        MOZ_ASSERT(!fullChunks(lock).contains(chunk));
-        MOZ_ASSERT(!availableChunks(lock).contains(chunk));
-        if (freeChunkCount >= tunables.maxEmptyChunkCount() ||
-            (freeChunkCount >= tunables.minEmptyChunkCount(lock) &&
-             (shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
-        {
-            emptyChunks(lock).remove(chunk);
-            prepareToFreeChunk(chunk->info);
-            expired.push(chunk);
-        } else {
-            /* Keep the chunk but increase its age. */
-            ++freeChunkCount;
-            ++chunk->info.age;
-        }
-    }
+    while (emptyChunks(lock).count() > tunables.minEmptyChunkCount(lock)) {
+        Chunk* chunk = emptyChunks(lock).pop();
+        prepareToFreeChunk(chunk->info);
+        expired.push(chunk);
+    }
+
     MOZ_ASSERT(expired.verify());
     MOZ_ASSERT(emptyChunks(lock).verify());
     MOZ_ASSERT(emptyChunks(lock).count() <= tunables.maxEmptyChunkCount());
-    MOZ_ASSERT_IF(shrinkBuffers, emptyChunks(lock).count() <= tunables.minEmptyChunkCount(lock));
+    MOZ_ASSERT(emptyChunks(lock).count() <= tunables.minEmptyChunkCount(lock));
     return expired;
 }
 
 static void
 FreeChunkPool(JSRuntime* rt, ChunkPool& pool)
 {
     for (ChunkPool::Iter iter(pool); !iter.done();) {
         Chunk* chunk = iter.get();
@@ -2604,17 +2584,16 @@ GCRuntime::unprotectHeldRelocatedArenas(
     }
 }
 
 void
 GCRuntime::releaseRelocatedArenas(Arena* arenaList)
 {
     AutoLockGC lock(rt);
     releaseRelocatedArenasWithoutUnlocking(arenaList, lock);
-    expireChunksAndArenas(true, lock);
 }
 
 void
 GCRuntime::releaseRelocatedArenasWithoutUnlocking(Arena* arenaList, const AutoLockGC& lock)
 {
     // Release the relocated arenas, now containing only forwarding pointers
     unsigned count = 0;
     while (arenaList) {
@@ -3191,25 +3170,21 @@ js::gc::BackgroundDecommitTask::run()
             // If we are low enough on memory that we can't update the page
             // tables, or if we need to return for any other reason, break out
             // of the loop.
             if (cancel_ || !ok)
                 break;
         }
     }
     toDecommit.clearAndFree();
-}
-
-void
-GCRuntime::expireChunksAndArenas(bool shouldShrink, AutoLockGC& lock)
-{
-    ChunkPool toFree = expireEmptyChunkPool(shouldShrink, lock);
+
+    ChunkPool toFree = runtime->gc.expireEmptyChunkPool(lock);
     if (toFree.count()) {
         AutoUnlockGC unlock(lock);
-        FreeChunkPool(rt, toFree);
+        FreeChunkPool(runtime, toFree);
     }
 }
 
 void
 GCRuntime::sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks, ThreadType threadType)
 {
     freeBlocks.freeAll();
 
@@ -3403,66 +3378,44 @@ GCHelperState::maybeStartBackgroundSweep
 {
     MOZ_ASSERT(CanUseExtraThreads());
 
     if (state() == IDLE)
         startBackgroundThread(SWEEPING);
 }
 
 void
-GCHelperState::startBackgroundShrink(const AutoLockGC& lock)
-{
-    MOZ_ASSERT(CanUseExtraThreads());
-    switch (state()) {
-      case IDLE:
-        shrinkFlag = true;
-        startBackgroundThread(SWEEPING);
-        break;
-      case SWEEPING:
-        shrinkFlag = true;
-        break;
-      default:
-        MOZ_CRASH("Invalid GC helper thread state.");
-    }
-}
-
-void
 GCHelperState::waitBackgroundSweepEnd()
 {
     AutoLockGC lock(rt);
     while (state() == SWEEPING)
         waitForBackgroundThread(lock);
     if (!rt->gc.isIncrementalGCInProgress())
         rt->gc.assertBackgroundSweepingFinished();
 }
 
 void
 GCHelperState::doSweep(AutoLockGC& lock)
 {
-    // The main thread may call queueZonesForBackgroundSweep() or
-    // ShrinkGCBuffers() while this is running so we must check there is no more
-    // work to do before exiting.
+    // The main thread may call queueZonesForBackgroundSweep() while this is
+    // running so we must check there is no more work to do before exiting.
 
     do {
         while (!rt->gc.backgroundSweepZones.isEmpty()) {
             AutoSetThreadIsSweeping threadIsSweeping;
 
             ZoneList zones;
             zones.transferFrom(rt->gc.backgroundSweepZones);
             LifoAlloc freeLifoAlloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
             freeLifoAlloc.transferFrom(&rt->gc.blocksToFreeAfterSweeping);
 
             AutoUnlockGC unlock(lock);
             rt->gc.sweepBackgroundThings(zones, freeLifoAlloc, BackgroundThread);
         }
-
-        bool shrinking = shrinkFlag;
-        shrinkFlag = false;
-        rt->gc.expireChunksAndArenas(shrinking, lock);
-    } while (!rt->gc.backgroundSweepZones.isEmpty() || shrinkFlag);
+    } while (!rt->gc.backgroundSweepZones.isEmpty());
 }
 
 bool
 GCHelperState::onBackgroundThread()
 {
     return PR_GetCurrentThread() == thread;
 }
 
@@ -5477,34 +5430,16 @@ GCRuntime::endSweepPhase(bool destroying
         gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_END);
         callFinalizeCallbacks(&fop, JSFINALIZE_COLLECTION_END);
 
         /* If we finished a full GC, then the gray bits are correct. */
         if (isFull)
             grayBitsValid = true;
     }
 
-    /* If not sweeping on background thread then we must do it here. */
-    if (!sweepOnBackgroundThread) {
-        gcstats::AutoPhase ap(stats, gcstats::PHASE_DESTROY);
-
-        assertBackgroundSweepingFinished();
-
-        /*
-         * Destroy arenas after we finished the sweeping so finalizers can
-         * safely use IsAboutToBeFinalized(). This is done on the
-         * GCHelperState if possible. We acquire the lock only because
-         * Expire needs to unlock it for other callers.
-         */
-        {
-            AutoLockGC lock(rt);
-            expireChunksAndArenas(invocationKind == GC_SHRINK, lock);
-        }
-    }
-
     finishMarkingValidation();
 
 #ifdef DEBUG
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         for (auto i : AllAllocKinds()) {
             MOZ_ASSERT_IF(!IsBackgroundFinalized(i) ||
                           !sweepOnBackgroundThread,
                           !zone->arenas.arenaListsToSweep[i]);
@@ -5598,22 +5533,16 @@ GCRuntime::finishCollection(JS::gcreason
         }
 
         MOZ_ASSERT(!zone->isCollecting());
         MOZ_ASSERT(!zone->wasGCStarted());
     }
 
     MOZ_ASSERT(zonesToMaybeCompact.isEmpty());
 
-    if (invocationKind == GC_SHRINK) {
-        // Ensure excess chunks are returns to the system and free arenas
-        // decommitted.
-        shrinkBuffers();
-    }
-
     lastGCTime = currentTime;
 }
 
 static const char*
 HeapStateToLabel(JS::HeapState heapState)
 {
     switch (heapState) {
       case JS::HeapState::MinorCollecting:
@@ -6501,35 +6430,16 @@ GCRuntime::debugGCSlice(SliceBudget& bud
 /* Schedule a full GC unless a zone will already be collected. */
 void
 js::PrepareForDebugGC(JSRuntime* rt)
 {
     if (!ZonesSelected(rt))
         JS::PrepareForFullGC(rt->contextFromMainThread());
 }
 
-JS_PUBLIC_API(void)
-JS::ShrinkGCBuffers(JSContext* cx)
-{
-    MOZ_ASSERT(!cx->isHeapBusy());
-    cx->gc.shrinkBuffers();
-}
-
-void
-GCRuntime::shrinkBuffers()
-{
-    AutoLockHelperThreadState helperLock;
-    AutoLockGC lock(rt);
-
-    if (CanUseExtraThreads())
-        helperState.startBackgroundShrink(lock);
-    else
-        expireChunksAndArenas(true, lock);
-}
-
 void
 GCRuntime::onOutOfMallocMemory()
 {
     // Stop allocating new chunks.
     allocTask.cancel(GCParallelTask::CancelAndWait);
 
     // Make sure we release anything queued for release.
     decommitTask.join();
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -860,36 +860,33 @@ class GCHelperState
     PRThread* thread;
 
     void startBackgroundThread(State newState);
     void waitForBackgroundThread(js::AutoLockGC& lock);
 
     State state();
     void setState(State state);
 
-    bool shrinkFlag;
-
     friend class js::gc::ArenaLists;
 
     static void freeElementsAndArray(void** array, void** end) {
         MOZ_ASSERT(array <= end);
         for (void** p = array; p != end; ++p)
             js_free(*p);
         js_free(array);
     }
 
     void doSweep(AutoLockGC& lock);
 
   public:
     explicit GCHelperState(JSRuntime* rt)
       : rt(rt),
         done(),
         state_(IDLE),
-        thread(nullptr),
-        shrinkFlag(false)
+        thread(nullptr)
     { }
 
     void finish();
 
     void work();
 
     void maybeStartBackgroundSweep(const AutoLockGC& lock);
     void startBackgroundShrink(const AutoLockGC& lock);
@@ -901,21 +898,16 @@ class GCHelperState
 
     /*
      * Outside the GC lock may give true answer when in fact the sweeping has
      * been done.
      */
     bool isBackgroundSweeping() const {
         return state_ == SWEEPING;
     }
-
-    bool shouldShrink() const {
-        MOZ_ASSERT(isBackgroundSweeping());
-        return shrinkFlag;
-    }
 };
 
 // A generic task used to dispatch work to the helper thread system.
 // Users should derive from GCParallelTask add what data they need and
 // override |run|.
 class GCParallelTask
 {
     // The state of the parallel computation.