diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 76912191ff3..fafa3d332b7 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -185,6 +185,7 @@ static uint32_t sForgetSkippableBeforeCC = 0; static uint32_t sPreviousSuspectedCount = 0; static uint32_t sCleanupsSinceLastGC = UINT32_MAX; static bool sNeedsFullCC = false; +static bool sNeedsFullGC = false; static bool sNeedsGCAfterCC = false; static bool sIncrementalCC = false; static bool sDidPaintAfterPreviousICCSlice = false; @@ -1462,7 +1463,13 @@ nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason, return; } - JS::PrepareForFullGC(sRuntime); + if (sNeedsFullGC || aReason != JS::gcreason::CC_WAITING) { + sNeedsFullGC = false; + JS::PrepareForFullGC(sRuntime); + } else { + CycleCollectedJSRuntime::Get()->PrepareWaitingZonesForGC(); + } + if (aIncremental == IncrementalGC) { MOZ_ASSERT(aShrinking == NonShrinkingGC); JS::IncrementalGC(sRuntime, aReason, aSliceMillis); @@ -2149,6 +2156,8 @@ nsJSContext::RunNextCollectorTimer() void nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay) { + sNeedsFullGC = sNeedsFullGC || aReason != JS::gcreason::CC_WAITING; + if (sGCTimer || sInterSliceGCTimer || sShuttingDown) { // There's already a timer for GC'ing, just return return; @@ -2458,6 +2467,7 @@ mozilla::dom::StartupJSEnvironment() sLikelyShortLivingObjectsNeedingGC = 0; sPostGCEventsToConsole = false; sNeedsFullCC = false; + sNeedsFullGC = false; sNeedsGCAfterCC = false; gNameSpaceManager = nullptr; sRuntimeService = nullptr; diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 195577832fc..fa1dcf3377b 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1260,6 +1260,7 @@ CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus) switch (aStatus) { case JSGC_BEGIN: nsCycleCollector_prepareForGarbageCollection(); + mZonesWaitingForGC.Clear(); break; case JSGC_END: { #ifdef MOZ_CRASHREPORTER @@ -1298,3 +1299,21 @@ CycleCollectedJSRuntime::OnLargeAllocationFailure() CustomLargeAllocationFailureCallback(); AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reported); } + +static PLDHashOperator +PrepareWaitingZoneForGC(nsPtrHashKey* aZoneEntry, void*) +{ + JS::PrepareZoneForGC(aZoneEntry->GetKey()); + return PL_DHASH_NEXT; +} + +void +CycleCollectedJSRuntime::PrepareWaitingZonesForGC() +{ + if (mZonesWaitingForGC.Count() == 0) { + JS::PrepareForFullGC(Runtime()); + } else { + mZonesWaitingForGC.EnumerateEntries(PrepareWaitingZoneForGC, nullptr); + mZonesWaitingForGC.Clear(); + } +} diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index 4923ce26a88..a5e603c173d 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -15,6 +15,7 @@ #include "nsDataHashtable.h" #include "nsHashKeys.h" #include "nsTArray.h" +#include "nsTHashtable.h" class nsCycleCollectionNoteRootCallback; class nsIException; @@ -291,6 +292,18 @@ public: // isn't one. static CycleCollectedJSRuntime* Get(); + // Add aZone to the set of zones waiting for a GC. + void AddZoneWaitingForGC(JS::Zone* aZone) + { + mZonesWaitingForGC.PutEntry(aZone); + } + + // Prepare any zones for GC that have been passed to AddZoneWaitingForGC() + // since the last GC or since the last call to PrepareWaitingZonesForGC(), + // whichever was most recent. If there were no such zones, prepare for a + // full GC. + void PrepareWaitingZonesForGC(); + private: JSGCThingParticipant mGCThingCycleCollectorGlobal; @@ -313,6 +326,8 @@ private: OOMState mOutOfMemoryState; OOMState mLargeAllocationFailureState; + + nsTHashtable> mZonesWaitingForGC; }; MOZ_FINISH_NESTED_ENUM_CLASS(CycleCollectedJSRuntime::OOMState) diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 01f61cb27e5..61ecefb9e65 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -3213,9 +3213,14 @@ nsCycleCollector::CollectWhite() if (pinfo->mColor == white && pinfo->mParticipant) { if (pinfo->IsGrayJS()) { ++numWhiteGCed; + JS::Zone* zone; if (MOZ_UNLIKELY(pinfo->mParticipant == zoneParticipant)) { ++numWhiteJSZones; + zone = static_cast(pinfo->mPointer); + } else { + zone = JS::GetTenuredGCThingZone(pinfo->mPointer); } + mJSRuntime->AddZoneWaitingForGC(zone); } else { whiteNodes.InfallibleAppend(pinfo); pinfo->mParticipant->Root(pinfo->mPointer);