Fix GC/CC scheduling (bug 630932, patch by gal/smaug, r=mrbkap). a=blocker

This commit is contained in:
Andreas Gal 2011-02-16 15:47:12 -08:00
Родитель c296ef9863
Коммит cd71f1272a
7 изменённых файлов: 212 добавлений и 359 удалений

Просмотреть файл

@ -655,7 +655,8 @@ nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener)
}
#endif
nsJSContext::CC(aListener, PR_TRUE);
nsJSContext::GarbageCollectNow();
nsJSContext::CycleCollectNow(aListener);
return NS_OK;
}

Просмотреть файл

@ -66,6 +66,7 @@
#include "jsdbgapi.h" // for JS_ClearWatchPointsForObject
#include "nsReadableUtils.h"
#include "nsDOMClassInfo.h"
#include "nsJSEnvironment.h"
// Other Classes
#include "nsIEventListenerManager.h"

Просмотреть файл

@ -131,60 +131,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
@ -193,11 +155,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;
@ -219,90 +179,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<nsIObserverService> 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;
}
@ -754,8 +648,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;
@ -1021,6 +913,7 @@ static const char js_methodjit_chrome_str[] = JS_OPTIONS_DOT_STR "methodjit.ch
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_methodjit_always_str[] = JS_OPTIONS_DOT_STR "methodjit_always";
static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
int
nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
@ -1029,6 +922,8 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions;
PRUint32 newDefaultJSOptions = oldDefaultJSOptions;
sPostGCEventsToConsole = nsContentUtils::GetBoolPref(js_memlog_option_str);
PRBool strict = nsContentUtils::GetBoolPref(js_strict_option_str);
if (strict)
newDefaultJSOptions |= JSOPTION_STRICT;
@ -1149,7 +1044,6 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime)
xpc_LocalizeContext(mContext);
}
mIsInitialized = PR_FALSE;
mNumEvaluations = 0;
mTerminations = nsnull;
mScriptsEnabled = PR_TRUE;
mOperationCallbackTime = 0;
@ -1199,7 +1093,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();
@ -3259,12 +3153,6 @@ nsJSContext::FinalizeContext()
;
}
void
nsJSContext::GC()
{
FireGCTimer(PR_FALSE);
}
void
nsJSContext::ScriptEvaluated(PRBool aTerminated)
{
@ -3282,17 +3170,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;
@ -3369,138 +3247,62 @@ 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: %lld, collected: %lu, suspected: %lu, duration: %llu ms.");
nsString msg;
msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), now,
collected, suspected,
(now - start) / PR_USEC_PER_MSEC));
nsCOMPtr<nsIConsoleService> 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
@ -3509,29 +3311,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;
}
@ -3539,24 +3335,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
@ -3568,30 +3364,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: %lld, duration: %llu ms.");
nsString msg;
msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), now,
(now - start) / PR_USEC_PER_MSEC));
nsCOMPtr<nsIConsoleService> 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())
@ -3692,18 +3584,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;
@ -3872,8 +3756,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);
@ -3935,15 +3817,10 @@ nsJSRuntime::Init()
nsCOMPtr<nsIObserverService> 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;
@ -3972,16 +3849,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);

Просмотреть файл

@ -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();

Просмотреть файл

@ -2379,7 +2379,7 @@ nsXPConnect::AfterProcessNextEvent(nsIThreadInternal *aThread,
{
// Call cycle collector occasionally.
if (NS_IsMainThread()) {
nsJSContext::MaybeCCIfUserInactive();
nsJSContext::MaybePokeCC();
}
return Pop(nsnull);

Просмотреть файл

@ -1293,6 +1293,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);

Просмотреть файл

@ -613,6 +613,7 @@ pref("javascript.options.mem.high_water_mark", 128);
pref("javascript.options.mem.max", -1);
pref("javascript.options.mem.gc_frequency", 300);
pref("javascript.options.mem.gc_per_compartment", true);
pref("javascript.options.mem.log", false);
// advanced prefs
pref("advanced.mailftp", false);