diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 99e072a2f634..a0b3dece1b22 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -144,7 +144,11 @@ static PRLogModuleInfo* gJSDiagnostics; #define NS_CC_SKIPPABLE_DELAY 400 // ms -#define NS_CC_FORCED (5 * 60 * PR_USEC_PER_SEC) // 5 min +// Force a CC after this long if there's anything in the purple buffer. +#define NS_CC_FORCED (2 * 60 * PR_USEC_PER_SEC) // 2 min + +// Trigger a CC if the purple buffer exceeds this size when we check it. +#define NS_CC_PURPLE_LIMIT 250 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT @@ -3286,46 +3290,70 @@ ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure) nsJSContext::ShrinkGCBuffersNow(); } -// static -void +static bool +ShouldTriggerCC(PRUint32 aSuspected) +{ + return sNeedsFullCC || + aSuspected > NS_CC_PURPLE_LIMIT || + sLastCCEndTime + NS_CC_FORCED < PR_Now(); +} + +static void +TimerFireForgetSkippable(PRUint32 aSuspected, bool aRemoveChildless) +{ + PRTime startTime = PR_Now(); + nsCycleCollector_forgetSkippable(aRemoveChildless); + sPreviousSuspectedCount = nsCycleCollector_suspectedCount(); + sCleanupSinceLastGC = true; + PRTime delta = PR_Now() - startTime; + if (sMinForgetSkippableTime > delta) { + sMinForgetSkippableTime = delta; + } + if (sMaxForgetSkippableTime < delta) { + sMaxForgetSkippableTime = delta; + } + sTotalForgetSkippableTime += delta; + sRemovedPurples += (aSuspected - sPreviousSuspectedCount); + ++sForgetSkippableBeforeCC; +} + +static void CCTimerFired(nsITimer *aTimer, void *aClosure) { - if (sDidShutdown) { - return; - } - if (sCCLockedOut) { + if (sDidShutdown || sCCLockedOut) { return; } ++sCCTimerFireCount; - if (sCCTimerFireCount < (NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY)) { - PRUint32 suspected = nsCycleCollector_suspectedCount(); - if ((sPreviousSuspectedCount + 100) > suspected) { - // Just few new suspected objects, return early. - return; - } - - PRTime startTime = PR_Now(); - nsCycleCollector_forgetSkippable(); - sPreviousSuspectedCount = nsCycleCollector_suspectedCount(); - sCleanupSinceLastGC = true; - PRTime delta = PR_Now() - startTime; - if (sMinForgetSkippableTime > delta) { - sMinForgetSkippableTime = delta; - } - if (sMaxForgetSkippableTime < delta) { - sMaxForgetSkippableTime = delta; - } - sTotalForgetSkippableTime += delta; - sRemovedPurples += (suspected - sPreviousSuspectedCount); - ++sForgetSkippableBeforeCC; - } else { - sPreviousSuspectedCount = 0; - nsJSContext::KillCCTimer(); - if (sNeedsFullCC || - nsCycleCollector_suspectedCount() > 500 || - sLastCCEndTime + NS_CC_FORCED < PR_Now()) { + + // During early timer fires, we only run forgetSkippable. During the first + // late timer fire, we decide if we are going to have a second and final + // late timer fire, where we may run the CC. + const PRUint32 numEarlyTimerFires = NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY - 2; + bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires; + PRUint32 suspected = nsCycleCollector_suspectedCount(); + if (isLateTimerFire && ShouldTriggerCC(suspected)) { + if (sCCTimerFireCount == numEarlyTimerFires + 1) { + TimerFireForgetSkippable(suspected, true); + if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) { + // Our efforts to avoid a CC have failed, so we return to let the + // timer fire once more to trigger a CC. + return; + } + } else { + // We are in the final timer fire and still meet the conditions for + // triggering a CC. nsJSContext::CycleCollectNow(); } + } else if ((sPreviousSuspectedCount + 100) <= suspected) { + // Only do a forget skippable if there are more than a few new objects. + TimerFireForgetSkippable(suspected, false); + } + + if (isLateTimerFire) { + // We have either just run the CC or decided we don't want to run the CC + // next time, so kill the timer. + sPreviousSuspectedCount = 0; + nsJSContext::KillCCTimer(); } }