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