Bug 727965 - Trigger CC_WAITING GCs based on number of freed JS objects and zones. r=smaug
authorAndrew McCreight <continuation@gmail.com>
Mon, 20 Oct 2014 10:07:52 -0700
changeset 211347 81d1dd9f566630f32298c1a8497a18093c266fb4
parent 211346 f1ecd7a2f170296d06afdfcb0436245573c0d0ba
child 211348 9f2c5c03252b9742876d16bb90e90cf2042e06f0
push id27673
push userkwierso@gmail.com
push dateTue, 21 Oct 2014 01:57:45 +0000
treeherdermozilla-central@29fbfc1b31aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs727965
milestone36.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 727965 - Trigger CC_WAITING GCs based on number of freed JS objects and zones. r=smaug
dom/base/nsJSEnvironment.cpp
xpcom/base/CycleCollectedJSRuntime.h
xpcom/base/nsCycleCollector.cpp
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -168,16 +168,17 @@ static bool sHasRunGC;
 // have an easy place to know when a load ends or is interrupted in
 // all cases. This counter also gets reset if we end up GC'ing while
 // we're waiting for a slow page to load. IOW, this count may be 0
 // even when there are pending loads.
 static uint32_t sPendingLoadCount;
 static bool sLoadingInProgress;
 
 static uint32_t sCCollectedWaitingForGC;
+static uint32_t sCCollectedZonesWaitingForGC;
 static uint32_t sLikelyShortLivingObjectsNeedingGC;
 static bool sPostGCEventsToConsole;
 static bool sPostGCEventsToObserver;
 static int32_t sCCTimerFireCount = 0;
 static uint32_t sMinForgetSkippableTime = UINT32_MAX;
 static uint32_t sMaxForgetSkippableTime = 0;
 static uint32_t sTotalForgetSkippableTime = 0;
 static uint32_t sRemovedPurples = 0;
@@ -241,16 +242,17 @@ KillTimers()
 }
 
 // If we collected a substantial amount of cycles, poke the GC since more objects
 // might be unreachable now.
 static bool
 NeedsGCAfterCC()
 {
   return sCCollectedWaitingForGC > 250 ||
+    sCCollectedZonesWaitingForGC > 0 ||
     sLikelyShortLivingObjectsNeedingGC > 2500 ||
     sNeedsGCAfterCC;
 }
 
 class nsJSEnvironmentObserver MOZ_FINAL : public nsIObserver
 {
   ~nsJSEnvironmentObserver() {}
 public:
@@ -1801,17 +1803,18 @@ nsJSContext::EndCycleCollectionCallback(
 
   nsJSContext::KillICCTimer();
 
   // Update timing information for the current slice before we log it, if
   // we previously called PrepareForCycleCollectionSlice(). During shutdown
   // CCs, this won't happen.
   gCCStats.FinishCycleCollectionSlice();
 
-  sCCollectedWaitingForGC += aResults.mFreedRefCounted + aResults.mFreedGCed;
+  sCCollectedWaitingForGC += aResults.mFreedGCed;
+  sCCollectedZonesWaitingForGC += aResults.mFreedJSZones;
 
   TimeStamp endCCTimeStamp = TimeStamp::Now();
   uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTimeStamp);
 
   if (NeedsGCAfterCC()) {
     PokeGC(JS::gcreason::CC_WAITING,
            NS_GC_DELAY - std::min(ccNowDuration, kMaxICCDuration));
   }
@@ -1850,25 +1853,25 @@ nsJSContext::EndCycleCollectionCallback(
     }
 
     nsCString gcMsg;
     if (aResults.mForcedGC) {
       gcMsg.AssignLiteral(", forced a GC");
     }
 
     NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
-      MOZ_UTF16("CC(T+%.1f) max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu waiting for GC)%s\n")
+      MOZ_UTF16("CC(T+%.1f) max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n")
       MOZ_UTF16("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu"));
     nsString msg;
     msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
                                         gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
                                         aResults.mNumSlices, gCCStats.mSuspected,
                                         aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
                                         aResults.mFreedRefCounted, aResults.mFreedGCed,
-                                        sCCollectedWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
+                                        sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
                                         gcMsg.get(),
                                         sForgetSkippableBeforeCC,
                                         minForgetSkippableTime / PR_USEC_PER_MSEC,
                                         sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
                                         (sTotalForgetSkippableTime / cleanups) /
                                           PR_USEC_PER_MSEC,
                                         sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
                                         gCCStats.mMaxSkippableDuration, sRemovedPurples));
@@ -1890,16 +1893,17 @@ nsJSContext::EndCycleCollectionCallback(
          MOZ_UTF16("\"suspected\": %lu, ")
          MOZ_UTF16("\"visited\": { ")
              MOZ_UTF16("\"RCed\": %lu, ")
              MOZ_UTF16("\"GCed\": %lu }, ")
          MOZ_UTF16("\"collected\": { ")
              MOZ_UTF16("\"RCed\": %lu, ")
              MOZ_UTF16("\"GCed\": %lu }, ")
          MOZ_UTF16("\"waiting_for_gc\": %lu, ")
+         MOZ_UTF16("\"zones_waiting_for_gc\": %lu, ")
          MOZ_UTF16("\"short_living_objects_waiting_for_gc\": %lu, ")
          MOZ_UTF16("\"forced_gc\": %d, ")
          MOZ_UTF16("\"forget_skippable\": { ")
              MOZ_UTF16("\"times_before_cc\": %lu, ")
              MOZ_UTF16("\"min\": %lu, ")
              MOZ_UTF16("\"max\": %lu, ")
              MOZ_UTF16("\"avg\": %lu, ")
              MOZ_UTF16("\"total\": %lu, ")
@@ -1910,16 +1914,17 @@ nsJSContext::EndCycleCollectionCallback(
                                          gCCStats.mMaxSliceTime,
                                          gCCStats.mTotalSliceTime,
                                          gCCStats.mMaxGCDuration,
                                          gCCStats.mMaxSkippableDuration,
                                          gCCStats.mSuspected,
                                          aResults.mVisitedRefCounted, aResults.mVisitedGCed,
                                          aResults.mFreedRefCounted, aResults.mFreedGCed,
                                          sCCollectedWaitingForGC,
+                                         sCCollectedZonesWaitingForGC,
                                          sLikelyShortLivingObjectsNeedingGC,
                                          aResults.mForcedGC,
                                          sForgetSkippableBeforeCC,
                                          minForgetSkippableTime / PR_USEC_PER_MSEC,
                                          sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
                                          (sTotalForgetSkippableTime / cleanups) /
                                            PR_USEC_PER_MSEC,
                                          sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
@@ -2365,16 +2370,17 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::G
     }
   }
 
   if (aProgress == JS::GC_CYCLE_END) {
     // May need to kill the inter-slice GC timer
     nsJSContext::KillInterSliceGCTimer();
 
     sCCollectedWaitingForGC = 0;
+    sCCollectedZonesWaitingForGC = 0;
     sLikelyShortLivingObjectsNeedingGC = 0;
     sCleanupsSinceLastGC = 0;
     sNeedsFullCC = true;
     sHasRunGC = true;
     nsJSContext::MaybePokeCC();
 
     if (aDesc.isCompartment_) {
       if (!sFullGCTimer && !sShuttingDown) {
@@ -2447,16 +2453,17 @@ mozilla::dom::StartupJSEnvironment()
   sGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr;
   sCCLockedOut = false;
   sCCLockedOutTime = 0;
   sLastCCEndTime = TimeStamp();
   sHasRunGC = false;
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
   sCCollectedWaitingForGC = 0;
+  sCCollectedZonesWaitingForGC = 0;
   sLikelyShortLivingObjectsNeedingGC = 0;
   sPostGCEventsToConsole = false;
   sNeedsFullCC = false;
   sNeedsGCAfterCC = false;
   gNameSpaceManager = nullptr;
   sRuntimeService = nullptr;
   sRuntime = nullptr;
   sIsInitialized = false;
--- a/xpcom/base/CycleCollectedJSRuntime.h
+++ b/xpcom/base/CycleCollectedJSRuntime.h
@@ -88,27 +88,29 @@ struct CycleCollectorResults
   void Init()
   {
     mForcedGC = false;
     mMergedZones = false;
     mVisitedRefCounted = 0;
     mVisitedGCed = 0;
     mFreedRefCounted = 0;
     mFreedGCed = 0;
+    mFreedJSZones = 0;
     mNumSlices = 1;
     // mNumSlices is initialized to one, because we call Init() after the
     // per-slice increment of mNumSlices has already occurred.
   }
 
   bool mForcedGC;
   bool mMergedZones;
   uint32_t mVisitedRefCounted;
   uint32_t mVisitedGCed;
   uint32_t mFreedRefCounted;
   uint32_t mFreedGCed;
+  uint32_t mFreedJSZones;
   uint32_t mNumSlices;
 };
 
 class CycleCollectedJSRuntime
 {
   friend class JSGCThingParticipant;
   friend class JSZoneParticipant;
   friend class IncrementalFinalizeRunnable;
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -3237,34 +3237,43 @@ nsCycleCollector::CollectWhite()
 
   TimeLog timeLog;
   nsAutoTArray<PtrInfo*, 4000> whiteNodes;
 
   MOZ_ASSERT(mIncrementalPhase == ScanAndCollectWhitePhase);
 
   whiteNodes.SetCapacity(mWhiteNodeCount);
   uint32_t numWhiteGCed = 0;
+  uint32_t numWhiteJSZones = 0;
+
+  bool hasJSRuntime = !!mJSRuntime;
+  nsCycleCollectionParticipant* zoneParticipant =
+    hasJSRuntime ? mJSRuntime->ZoneParticipant() : nullptr;
 
   NodePool::Enumerator etor(mGraph.mNodes);
   while (!etor.IsDone()) {
     PtrInfo* pinfo = etor.GetNext();
     if (pinfo->mColor == white && pinfo->mParticipant) {
       whiteNodes.AppendElement(pinfo);
       pinfo->mParticipant->Root(pinfo->mPointer);
       if (pinfo->IsGrayJS()) {
         ++numWhiteGCed;
+        if (MOZ_UNLIKELY(pinfo->mParticipant == zoneParticipant)) {
+          ++numWhiteJSZones;
+        }
       }
     }
   }
 
   uint32_t count = whiteNodes.Length();
   MOZ_ASSERT(numWhiteGCed <= count,
              "More freed GCed nodes than total freed nodes.");
   mResults.mFreedRefCounted += count - numWhiteGCed;
   mResults.mFreedGCed += numWhiteGCed;
+  mResults.mFreedJSZones += numWhiteJSZones;
 
   timeLog.Checkpoint("CollectWhite::Root");
 
   if (mBeforeUnlinkCB) {
     mBeforeUnlinkCB();
     timeLog.Checkpoint("CollectWhite::BeforeUnlinkCB");
   }