From ddbfec9fde5f76d838303ebc9706c6519c69f6a1 Mon Sep 17 00:00:00 2001 From: "gal@uci.edu" Date: Fri, 11 Feb 2011 17:12:47 +0200 Subject: [PATCH] Bug 630932, change CC/GC scheduling, r=smaug, a=blocker --- dom/base/nsDOMWindowUtils.cpp | 3 +- dom/base/nsGlobalWindow.cpp | 1 + dom/base/nsJSEnvironment.cpp | 522 ++++++++++----------------- dom/base/nsJSEnvironment.h | 38 +- js/src/xpconnect/src/nsXPConnect.cpp | 2 +- layout/base/nsDocumentViewer.cpp | 3 + modules/libpref/src/init/all.js | 1 + 7 files changed, 211 insertions(+), 359 deletions(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index ec2ac5242582..c5afed5d702b 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -655,7 +655,8 @@ nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener) } #endif - nsJSContext::CC(aListener, PR_TRUE); + nsJSContext::GarbageCollectNow(); + nsJSContext::CycleCollectNow(aListener); return NS_OK; } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 33fe1a6ebb32..d1ba2161c9a8 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -66,6 +66,7 @@ #include "jsdbgapi.h" // for JS_ClearWatchPointsForObject #include "nsReadableUtils.h" #include "nsDOMClassInfo.h" +#include "nsJSEnvironment.h" // Other Classes #include "nsIEventListenerManager.h" diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index a1838ed73c04..4b8e65cdef21 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -130,60 +130,22 @@ static PRLogModuleInfo* gJSDiagnostics; // The amount of time we wait between a request to GC (due to leaving // a page) and doing the actual GC. -#define NS_GC_DELAY 2000 // ms - -// The amount of time we wait until we force a GC in case the previous -// GC timer happened to fire while we were in the middle of loading a -// page (we'll GC once the page is loaded if that happens before this -// amount of time has passed). -#define NS_LOAD_IN_PROCESS_GC_DELAY 4000 // ms +#define NS_GC_DELAY 4000 // ms // The amount of time we wait from the first request to GC to actually // doing the first GC. #define NS_FIRST_GC_DELAY 10000 // ms -#define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT +// The amount of time we wait between a request to CC (after GC ran) +// and doing the actual CC. +#define NS_CC_DELAY 5000 // ms -// The max number of delayed cycle collects.. -#define NS_MAX_DELAYED_CCOLLECT 45 -// The max number of user interaction notifications in inactive state before -// we try to call cycle collector more aggressively. -#define NS_CC_SOFT_LIMIT_INACTIVE 6 -// The max number of user interaction notifications in active state before -// we try to call cycle collector more aggressively. -#define NS_CC_SOFT_LIMIT_ACTIVE 12 -// When higher probability MaybeCC is used, the number of sDelayedCCollectCount -// is multiplied with this number. -#define NS_PROBABILITY_MULTIPLIER 3 -// Cycle collector is never called more often than every NS_MIN_CC_INTERVAL -// milliseconds. Exceptions are low memory situation and memory pressure -// notification. -#define NS_MIN_CC_INTERVAL 10000 // ms -// If previous cycle collection collected more than this number of objects, -// the next collection will happen somewhat soon. -// Also, if there are more than this number suspected objects, GC will be called -// right before CC, if it wasn't called after last CC. -#define NS_COLLECTED_OBJECTS_LIMIT 5000 -// CC will be called if GC has been called at least this number of times and -// there are at least NS_MIN_SUSPECT_CHANGES new suspected objects. -#define NS_MAX_GC_COUNT 5 -#define NS_MIN_SUSPECT_CHANGES 100 -// CC will be called if there are at least NS_MAX_SUSPECT_CHANGES new suspected -// objects. -#define NS_MAX_SUSPECT_CHANGES 1000 +#define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT // if you add statics here, add them to the list in nsJSRuntime::Startup -static PRUint32 sDelayedCCollectCount; -static PRUint32 sCCollectCount; -static PRBool sUserIsActive; -static PRTime sPreviousCCTime; -static PRUint32 sCollectedObjectsCounts; -static PRUint32 sSavedGCCount; -static PRUint32 sCCSuspectChanges; -static PRUint32 sCCSuspectedCount; static nsITimer *sGCTimer; -static PRBool sReadyForGC; +static nsITimer *sCCTimer; // The number of currently pending document loads. This count isn't // guaranteed to always reflect reality and can't easily as we don't @@ -192,11 +154,9 @@ static PRBool sReadyForGC; // we're waiting for a slow page to load. IOW, this count may be 0 // even when there are pending loads. static PRUint32 sPendingLoadCount; +static PRBool sLoadingInProgress; -// Boolean that tells us whether or not the current GC timer -// (sGCTimer) was scheduled due to a GC timer firing while we were in -// the middle of loading a page. -static PRBool sLoadInProgressGCTimer; +static PRBool sPostGCEventsToConsole; nsScriptNameSpaceManager *gNameSpaceManager; @@ -218,90 +178,24 @@ static PRTime sMaxChromeScriptRunTime; static nsIScriptSecurityManager *sSecurityManager; -// nsUserActivityObserver observes user-interaction-active and -// user-interaction-inactive notifications. It counts the number of -// notifications and if the number is bigger than NS_CC_SOFT_LIMIT_ACTIVE -// (in case the current notification is user-interaction-active) or -// NS_CC_SOFT_LIMIT_INACTIVE (current notification is user-interaction-inactive) -// MaybeCC is called with aHigherParameter set to PR_TRUE, otherwise PR_FALSE. -// -// When moving from active state to inactive, nsJSContext::IntervalCC() is -// called unless the timer related to page load is active. +// nsMemoryPressureObserver observes the memory-pressure notifications +// and forces a garbage collection and cycle collection when it happens. -class nsUserActivityObserver : public nsIObserver -{ -public: - nsUserActivityObserver() - : mUserActivityCounter(0), mOldCCollectCount(0) {} - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER -private: - PRUint32 mUserActivityCounter; - PRUint32 mOldCCollectCount; -}; - -NS_IMPL_ISUPPORTS1(nsUserActivityObserver, nsIObserver) - -NS_IMETHODIMP -nsUserActivityObserver::Observe(nsISupports* aSubject, const char* aTopic, - const PRUnichar* aData) -{ - if (mOldCCollectCount != sCCollectCount) { - mOldCCollectCount = sCCollectCount; - // Cycle collector was called between user interaction notifications, so - // we can reset the counter. - mUserActivityCounter = 0; - } - PRBool higherProbability = PR_FALSE; - ++mUserActivityCounter; - if (!strcmp(aTopic, "user-interaction-inactive")) { -#ifdef DEBUG_smaug - printf("user-interaction-inactive\n"); -#endif - if (sUserIsActive) { - sUserIsActive = PR_FALSE; - if (!sGCTimer) { - nsJSContext::MaybeCC(PR_FALSE, PR_TRUE); - return NS_OK; - } - } - higherProbability = (mUserActivityCounter > NS_CC_SOFT_LIMIT_INACTIVE); - } else if (!strcmp(aTopic, "user-interaction-active")) { -#ifdef DEBUG_smaug - printf("user-interaction-active\n"); -#endif - sUserIsActive = PR_TRUE; - higherProbability = (mUserActivityCounter > NS_CC_SOFT_LIMIT_ACTIVE); - } else if (!strcmp(aTopic, "xpcom-shutdown")) { - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, "user-interaction-active"); - obs->RemoveObserver(this, "user-interaction-inactive"); - obs->RemoveObserver(this, "xpcom-shutdown"); - } - return NS_OK; - } - nsJSContext::MaybeCC(higherProbability); - return NS_OK; -} - -// nsCCMemoryPressureObserver observes the memory-pressure notifications -// and forces a cycle collection when it happens. - -class nsCCMemoryPressureObserver : public nsIObserver +class nsMemoryPressureObserver : public nsIObserver { public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER }; -NS_IMPL_ISUPPORTS1(nsCCMemoryPressureObserver, nsIObserver) +NS_IMPL_ISUPPORTS1(nsMemoryPressureObserver, nsIObserver) NS_IMETHODIMP -nsCCMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic, - const PRUnichar* aData) +nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic, + const PRUnichar* aData) { - nsJSContext::CC(nsnull, PR_TRUE); + nsJSContext::GarbageCollectNow(); + nsJSContext::CycleCollectNow(); return NS_OK; } @@ -753,8 +647,6 @@ nsJSContext::DOMOperationCallback(JSContext *cx) PRTime callbackTime = ctx->mOperationCallbackTime; PRTime modalStateTime = ctx->mModalStateTime; - JS_MaybeGC(cx); - // Now restore the callback time and count, in case they got reset. ctx->mOperationCallbackTime = callbackTime; ctx->mModalStateTime = modalStateTime; @@ -1019,10 +911,13 @@ static const char js_methodjit_content_str[] = JS_OPTIONS_DOT_STR "methodjit.c static const char js_methodjit_chrome_str[] = JS_OPTIONS_DOT_STR "methodjit.chrome"; static const char js_profiling_content_str[] = JS_OPTIONS_DOT_STR "jitprofiling.content"; static const char js_profiling_chrome_str[] = JS_OPTIONS_DOT_STR "jitprofiling.chrome"; +static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log"; int nsJSContext::JSOptionChangedCallback(const char *pref, void *data) { + sPostGCEventsToConsole = nsContentUtils::GetBoolPref(js_memlog_option_str); + nsJSContext *context = reinterpret_cast(data); PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions; PRUint32 newDefaultJSOptions = oldDefaultJSOptions; @@ -1140,7 +1035,6 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) xpc_LocalizeContext(mContext); } mIsInitialized = PR_FALSE; - mNumEvaluations = 0; mTerminations = nsnull; mScriptsEnabled = PR_TRUE; mOperationCallbackTime = 0; @@ -1190,7 +1084,7 @@ nsJSContext::DestroyJSContext() JSOptionChangedCallback, this); - PRBool do_gc = mGCOnDestruction && !sGCTimer && sReadyForGC; + PRBool do_gc = mGCOnDestruction && !sGCTimer; // Let xpconnect destroy the JSContext when it thinks the time is right. nsIXPConnect *xpc = nsContentUtils::XPConnect(); @@ -3269,12 +3163,6 @@ nsJSContext::FinalizeContext() ; } -void -nsJSContext::GC() -{ - FireGCTimer(PR_FALSE); -} - void nsJSContext::ScriptEvaluated(PRBool aTerminated) { @@ -3292,17 +3180,7 @@ nsJSContext::ScriptEvaluated(PRBool aTerminated) delete start; } - mNumEvaluations++; - -#ifdef JS_GC_ZEAL - if (mContext->runtime->gcZeal >= 2) { - JS_MaybeGC(mContext); - } else -#endif - if (mNumEvaluations > 20) { - mNumEvaluations = 0; - JS_MaybeGC(mContext); - } + JS_MaybeGC(mContext); if (aTerminated) { mOperationCallbackTime = 0; @@ -3379,138 +3257,61 @@ nsJSContext::ScriptExecuted() return NS_OK; } -static inline uint32 -GetGCRunsSinceLastCC() -{ - // To avoid crash if nsJSRuntime is not properly initialized. - // See the bug 474586 - if (!nsJSRuntime::sRuntime) - return 0; - - // Since JS_GetGCParameter() and sSavedGCCount are unsigned, the following - // gives the correct result even when the GC counter wraps around - // UINT32_MAX since the last call to JS_GetGCParameter(). - return JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER) - - sSavedGCCount; -} - //static void -nsJSContext::CC(nsICycleCollectorListener *aListener, PRBool aForceGC) +nsJSContext::GarbageCollectNow() { NS_TIME_FUNCTION_MIN(1.0); - ++sCCollectCount; -#ifdef DEBUG_smaug - printf("Will run cycle collector (%i), %lldms since previous.\n", - sCCollectCount, (PR_Now() - sPreviousCCTime) / PR_USEC_PER_MSEC); -#endif - sPreviousCCTime = PR_Now(); - sDelayedCCollectCount = 0; - sCCSuspectChanges = 0; - // nsCycleCollector_collect() no longer forces a JS garbage collection, - // so we have to do it ourselves here. - if (nsContentUtils::XPConnect() && - (aForceGC || - (!GetGCRunsSinceLastCC() && - sCCSuspectedCount > NS_COLLECTED_OBJECTS_LIMIT))) { - nsContentUtils::XPConnect()->GarbageCollect(); - } - sCollectedObjectsCounts = nsCycleCollector_collect(aListener); - sCCSuspectedCount = nsCycleCollector_suspectedCount(); - if (nsJSRuntime::sRuntime) { - sSavedGCCount = JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER); - } -#ifdef DEBUG_smaug - printf("Collected %u objects, %u suspected objects, took %lldms\n", - sCollectedObjectsCounts, sCCSuspectedCount, - (PR_Now() - sPreviousCCTime) / PR_USEC_PER_MSEC); -#endif + KillGCTimer(); + + // 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; + sLoadingInProgress = PR_FALSE; + + nsContentUtils::XPConnect()->GarbageCollect(); } -//static -PRBool -nsJSContext::MaybeCC(PRBool aHigherProbability, PRBool aForceGC) +//Static +void +nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener) { - ++sDelayedCCollectCount; + if (!NS_IsMainThread()) { + return; + } - // Don't check suspected count if CC will be called anyway. - if (sCCSuspectChanges <= NS_MIN_SUSPECT_CHANGES || - GetGCRunsSinceLastCC() <= NS_MAX_GC_COUNT) { -#ifdef DEBUG_smaug + NS_TIME_FUNCTION_MIN(1.0); + + KillCCTimer(); + + PRTime start = PR_Now(); + + PRUint32 suspected = nsCycleCollector_suspectedCount(); + PRUint32 collected = nsCycleCollector_collect(aListener); + + // If we collected cycles, poke the GC since more objects might be unreachable now. + if (collected > 0) { + PokeGC(); + } + + if (sPostGCEventsToConsole) { PRTime now = PR_Now(); -#endif - PRUint32 suspected = nsCycleCollector_suspectedCount(); -#ifdef DEBUG_smaug - printf("%u suspected objects (%lldms), sCCSuspectedCount %u\n", - suspected, (PR_Now() - now) / PR_USEC_PER_MSEC, - sCCSuspectedCount); -#endif - // Update only when suspected count has increased. - if (suspected > sCCSuspectedCount) { - sCCSuspectChanges += (suspected - sCCSuspectedCount); - sCCSuspectedCount = suspected; + NS_NAMED_LITERAL_STRING(kFmt, "CC timestamp: %lu collected: %lu suspected: %lu duration: %lu ms."); + nsString msg; + msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), now, + collected, suspected, + (now - start) / PR_USEC_PER_MSEC)); + nsCOMPtr cs = + do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (cs) { + cs->LogStringMessage(msg.get()); } } -#ifdef DEBUG_smaug - printf("sCCSuspectChanges %u, GC runs %u\n", - sCCSuspectChanges, GetGCRunsSinceLastCC()); -#endif - - // Increase the probability also if the previous call to cycle collector - // collected something. - if (aHigherProbability || - sCollectedObjectsCounts > NS_COLLECTED_OBJECTS_LIMIT) { - sDelayedCCollectCount *= NS_PROBABILITY_MULTIPLIER; - } else if (!sUserIsActive && sCCSuspectChanges > NS_MAX_SUSPECT_CHANGES) { - // If user is inactive and there are lots of new suspected objects, - // increase the probability for cycle collection. - sDelayedCCollectCount += (sCCSuspectChanges / NS_MAX_SUSPECT_CHANGES); - } - - if (!sGCTimer && - (sDelayedCCollectCount > NS_MAX_DELAYED_CCOLLECT) && - ((sCCSuspectChanges > NS_MIN_SUSPECT_CHANGES && - GetGCRunsSinceLastCC() > NS_MAX_GC_COUNT) || - (sCCSuspectChanges > NS_MAX_SUSPECT_CHANGES))) { - return IntervalCC(aForceGC); - } - return PR_FALSE; -} - -//static -void -nsJSContext::CCIfUserInactive() -{ - if (sUserIsActive) { - MaybeCC(PR_TRUE, PR_TRUE); - } else { - IntervalCC(PR_TRUE); - } -} - -//static -void -nsJSContext::MaybeCCIfUserInactive() -{ - if (!sUserIsActive) { - MaybeCC(PR_FALSE); - } -} - -//static -PRBool -nsJSContext::IntervalCC(PRBool aForceGC) -{ - if ((PR_Now() - sPreviousCCTime) >= - PRTime(NS_MIN_CC_INTERVAL * PR_USEC_PER_MSEC)) { - nsJSContext::CC(nsnull, aForceGC); - return PR_TRUE; - } -#ifdef DEBUG_smaug - printf("Running CC was delayed because of NS_MIN_CC_INTERVAL.\n"); -#endif - return PR_FALSE; } // static @@ -3519,29 +3320,23 @@ GCTimerFired(nsITimer *aTimer, void *aClosure) { NS_RELEASE(sGCTimer); - if (sPendingLoadCount == 0 || sLoadInProgressGCTimer) { - sLoadInProgressGCTimer = PR_FALSE; + nsJSContext::GarbageCollectNow(); +} - // 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; +// static +void +CCTimerFired(nsITimer *aTimer, void *aClosure) +{ + NS_RELEASE(sCCTimer); - nsJSContext::CCIfUserInactive(); - } else { - nsJSContext::FireGCTimer(PR_TRUE); - } - - sReadyForGC = PR_TRUE; + nsJSContext::CycleCollectNow(); } // static void nsJSContext::LoadStart() { + sLoadingInProgress = PR_TRUE; ++sPendingLoadCount; } @@ -3549,24 +3344,24 @@ nsJSContext::LoadStart() void nsJSContext::LoadEnd() { + if (!sLoadingInProgress) + return; + // sPendingLoadCount is not a well managed load counter (and doesn't // need to be), so make sure we don't make it wrap backwards here. if (sPendingLoadCount > 0) { --sPendingLoadCount; + return; } - if (!sPendingLoadCount && sLoadInProgressGCTimer) { - sGCTimer->Cancel(); - NS_RELEASE(sGCTimer); - sLoadInProgressGCTimer = PR_FALSE; - - CCIfUserInactive(); - } + // Its probably a good idea to GC soon since we have finished loading. + sLoadingInProgress = PR_FALSE; + PokeGC(); } // static void -nsJSContext::FireGCTimer(PRBool aLoadInProgress) +nsJSContext::PokeGC() { if (sGCTimer) { // There's already a timer for GC'ing, just return @@ -3578,30 +3373,126 @@ nsJSContext::FireGCTimer(PRBool aLoadInProgress) if (!sGCTimer) { NS_WARNING("Failed to create timer"); - // Reset sLoadInProgressGCTimer since we're not able to fire the - // timer. - sLoadInProgressGCTimer = PR_FALSE; - - CCIfUserInactive(); + GarbageCollectNow(); return; } static PRBool first = PR_TRUE; sGCTimer->InitWithFuncCallback(GCTimerFired, nsnull, - first ? NS_FIRST_GC_DELAY : - aLoadInProgress ? NS_LOAD_IN_PROCESS_GC_DELAY : - NS_GC_DELAY, + first + ? NS_FIRST_GC_DELAY + : NS_GC_DELAY, nsITimer::TYPE_ONE_SHOT); - sLoadInProgressGCTimer = aLoadInProgress; - first = PR_FALSE; } +// static +void +nsJSContext::MaybePokeCC() +{ + if (nsCycleCollector_suspectedCount() > 1000) { + PokeCC(); + } +} + +// static +void +nsJSContext::PokeCC() +{ + if (sCCTimer) { + // There's already a timer for GC'ing, just return + return; + } + + CallCreateInstance("@mozilla.org/timer;1", &sCCTimer); + + if (!sCCTimer) { + NS_WARNING("Failed to create timer"); + + CycleCollectNow(); + return; + } + + sCCTimer->InitWithFuncCallback(CCTimerFired, nsnull, + NS_CC_DELAY, + nsITimer::TYPE_ONE_SHOT); +} + +//static +void +nsJSContext::KillGCTimer() +{ + if (sGCTimer) { + sGCTimer->Cancel(); + + NS_RELEASE(sGCTimer); + } +} + +//static +void +nsJSContext::KillCCTimer() +{ + if (sCCTimer) { + sCCTimer->Cancel(); + + NS_RELEASE(sCCTimer); + } +} + +void +nsJSContext::GC() +{ + PokeGC(); +} + static JSBool DOMGCCallback(JSContext *cx, JSGCStatus status) { + static PRTime start; + + if (sPostGCEventsToConsole && NS_IsMainThread()) { + if (status == JSGC_BEGIN) { + start = PR_Now(); + } else if (status == JSGC_END) { + PRTime now = PR_Now(); + NS_NAMED_LITERAL_STRING(kFmt, "GC timestamp: %lu duration: %lu ms."); + nsString msg; + msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), now, + (now - start) / PR_USEC_PER_MSEC)); + nsCOMPtr cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (cs) { + cs->LogStringMessage(msg.get()); + } + } + } + + if (status == JSGC_END) { + if (sGCTimer) { + // If we were waiting for a GC to happen, kill the timer. + nsJSContext::KillGCTimer(); + + // If this is a compartment GC, restart it. We still want + // a full GC to happen. Compartment GCs usually happen as a + // result of last-ditch or MaybeGC. In both cases its + // probably a time of heavy activity and we want to delay + // the full GC, but we do want it to happen eventually. + if (cx->runtime->gcTriggerCompartment) { + nsJSContext::PokeGC(); + + // We poked the GC, so we can kill any pending CC here. + nsJSContext::KillCCTimer(); + } + } else { + // If this was a full GC, poke the CC to run soon. + if (!cx->runtime->gcTriggerCompartment) { + nsJSContext::PokeCC(); + } + } + } + JSBool result = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; if (status == JSGC_BEGIN && !NS_IsMainThread()) @@ -3702,18 +3593,10 @@ void nsJSRuntime::Startup() { // initialize all our statics, so that we can restart XPCOM - sDelayedCCollectCount = 0; - sCCollectCount = 0; - sUserIsActive = PR_FALSE; - sPreviousCCTime = PR_Now(); - sCollectedObjectsCounts = 0; - sSavedGCCount = 0; - sCCSuspectChanges = 0; - sCCSuspectedCount = 0; - sGCTimer = nsnull; - sReadyForGC = PR_FALSE; - sLoadInProgressGCTimer = PR_FALSE; + sGCTimer = sCCTimer = nsnull; sPendingLoadCount = 0; + sLoadingInProgress = PR_FALSE; + sPostGCEventsToConsole = PR_FALSE; gNameSpaceManager = nsnull; sRuntimeService = nsnull; sRuntime = nsnull; @@ -3882,8 +3765,6 @@ nsJSRuntime::Init() NS_ASSERTION(!gOldJSGCCallback, "nsJSRuntime initialized more than once"); - sSavedGCCount = JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER); - // Save the old GC callback to chain to it, for GC-observing generality. gOldJSGCCallback = ::JS_SetGCCallbackRT(sRuntime, DOMGCCallback); @@ -3945,15 +3826,10 @@ nsJSRuntime::Init() nsCOMPtr obs = mozilla::services::GetObserverService(); if (!obs) return NS_ERROR_FAILURE; - nsIObserver* activityObserver = new nsUserActivityObserver(); - NS_ENSURE_TRUE(activityObserver, NS_ERROR_OUT_OF_MEMORY); - obs->AddObserver(activityObserver, "user-interaction-inactive", PR_FALSE); - obs->AddObserver(activityObserver, "user-interaction-active", PR_FALSE); - obs->AddObserver(activityObserver, "xpcom-shutdown", PR_FALSE); - nsIObserver* ccMemPressureObserver = new nsCCMemoryPressureObserver(); - NS_ENSURE_TRUE(ccMemPressureObserver, NS_ERROR_OUT_OF_MEMORY); - obs->AddObserver(ccMemPressureObserver, "memory-pressure", PR_FALSE); + nsIObserver* memPressureObserver = new nsMemoryPressureObserver(); + NS_ENSURE_TRUE(memPressureObserver, NS_ERROR_OUT_OF_MEMORY); + obs->AddObserver(memPressureObserver, "memory-pressure", PR_FALSE); sIsInitialized = PR_TRUE; @@ -3982,16 +3858,8 @@ nsJSRuntime::GetNameSpaceManager() void nsJSRuntime::Shutdown() { - if (sGCTimer) { - // We're being shut down, if we have a GC timer scheduled, cancel - // it. The DOM factory will do one final GC once it's shut down. - - sGCTimer->Cancel(); - - NS_RELEASE(sGCTimer); - - sLoadInProgressGCTimer = PR_FALSE; - } + nsJSContext::KillGCTimer(); + nsJSContext::KillCCTimer(); NS_IF_RELEASE(gNameSpaceManager); diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 0df5530daad7..306f8f174b11 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -148,8 +148,6 @@ public: virtual PRBool IsContextInitialized(); virtual void FinalizeContext(); - virtual void GC(); - virtual void ScriptEvaluated(PRBool aTerminated); virtual nsresult SetTerminationFunction(nsScriptTerminationFunc aFunc, nsISupports* aRef); @@ -187,37 +185,17 @@ public: static void LoadStart(); static void LoadEnd(); - // CC does always call cycle collector and it also updates the counters - // that MaybeCC uses. - static void CC(nsICycleCollectorListener *aListener, - PRBool aForceGC = PR_FALSE); + static void GarbageCollectNow(); + static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull); - // MaybeCC calls cycle collector if certain conditions are fulfilled. - // The conditions are: - // - The timer related to page load (sGCTimer) must not be active. - // - At least NS_MIN_CC_INTERVAL milliseconds must have elapsed since the - // previous cycle collector call. - // - Certain number of MaybeCC calls have occurred. - // The number of needed MaybeCC calls depends on the aHigherProbability - // parameter. If the parameter is true, probability for calling cycle - // collector rises increasingly. If the parameter is all the time false, - // at least NS_MAX_DELAYED_CCOLLECT MaybeCC calls are needed. - // If the previous call to cycle collector did collect something, - // MaybeCC works effectively as if aHigherProbability was true. - // @return PR_TRUE if cycle collector was called. - static PRBool MaybeCC(PRBool aHigherProbability, PRBool aForceGC = PR_FALSE); + static void PokeGC(); + static void KillGCTimer(); - // IntervalCC() calls CC() if at least NS_MIN_CC_INTERVAL milliseconds have - // elapsed since the previous cycle collector call. - static PRBool IntervalCC(PRBool aForceGC = PR_FALSE); + static void PokeCC(); + static void MaybePokeCC(); + static void KillCCTimer(); - // Calls IntervalCC(PR_TRUE) if user is currently inactive, - // otherwise MaybeCC(PR_TRUE, PR_TRUE) - static void CCIfUserInactive(); - - static void MaybeCCIfUserInactive(); - - static void FireGCTimer(PRBool aLoadInProgress); + virtual void GC(); protected: nsresult InitializeExternalClasses(); diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 0f2ddb2394aa..8531c36e4ac3 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -2336,7 +2336,7 @@ nsXPConnect::AfterProcessNextEvent(nsIThreadInternal *aThread, { // Call cycle collector occasionally. if (NS_IsMainThread()) { - nsJSContext::MaybeCCIfUserInactive(); + nsJSContext::MaybePokeCC(); } return Pop(nsnull); diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index f899bc3d9d91..e4e9ab1ad8a2 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -1289,6 +1289,9 @@ DocumentViewerImpl::PageHide(PRBool aIsUnload) window->PageHidden(); if (aIsUnload) { + // Poke the GC. The window might be collectable garbage now. + nsJSContext::PokeGC(); + // if Destroy() was called during OnPageHide(), mDocument is nsnull. NS_ENSURE_STATE(mDocument); diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 7d0f00ba5822..c8c5a3301faf 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -604,6 +604,7 @@ pref("javascript.options.methodjit.content", true); pref("javascript.options.methodjit.chrome", false); pref("javascript.options.jitprofiling.content", true); pref("javascript.options.jitprofiling.chrome", false); +pref("javascript.options.mem.log", 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