Bug 1130439 - Trigger compacting GCs after the user becomes inactive r=smaug r=terrence

This commit is contained in:
Jon Coppeard 2015-02-24 09:40:02 +00:00
Родитель 2c936db8ea
Коммит 2b3e9874b8
8 изменённых файлов: 94 добавлений и 20 удалений

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

@ -107,6 +107,10 @@ const size_t gStackSize = 8192;
#define NS_FULL_GC_DELAY 60000 // ms #define NS_FULL_GC_DELAY 60000 // ms
// The amount of time to wait from the user being idle to starting a shrinking
// GC.
#define NS_SHRINKING_GC_DELAY 15000 // ms
// Maximum amount of time that should elapse between incremental GC slices // Maximum amount of time that should elapse between incremental GC slices
#define NS_INTERSLICE_GC_DELAY 100 // ms #define NS_INTERSLICE_GC_DELAY 100 // ms
@ -148,6 +152,7 @@ static const uint32_t kMaxICCDuration = 2000; // ms
static nsITimer *sGCTimer; static nsITimer *sGCTimer;
static nsITimer *sShrinkGCBuffersTimer; static nsITimer *sShrinkGCBuffersTimer;
static nsITimer *sShrinkingGCTimer;
static nsITimer *sCCTimer; static nsITimer *sCCTimer;
static nsITimer *sICCTimer; static nsITimer *sICCTimer;
static nsITimer *sFullGCTimer; static nsITimer *sFullGCTimer;
@ -212,6 +217,7 @@ static nsIScriptSecurityManager *sSecurityManager;
// the appropriate pref is set. // the appropriate pref is set.
static bool sGCOnMemoryPressure; static bool sGCOnMemoryPressure;
static bool sCompactOnUserInactive;
// In testing, we call RunNextCollectorTimer() to ensure that the collectors are run more // In testing, we call RunNextCollectorTimer() to ensure that the collectors are run more
// aggressively than they would be in regular browsing. sExpensiveCollectorPokes keeps // aggressively than they would be in regular browsing. sExpensiveCollectorPokes keeps
@ -234,6 +240,7 @@ static void
KillTimers() KillTimers()
{ {
nsJSContext::KillGCTimer(); nsJSContext::KillGCTimer();
nsJSContext::KillShrinkingGCTimer();
nsJSContext::KillShrinkGCBuffersTimer(); nsJSContext::KillShrinkGCBuffersTimer();
nsJSContext::KillCCTimer(); nsJSContext::KillCCTimer();
nsJSContext::KillICCTimer(); nsJSContext::KillICCTimer();
@ -266,22 +273,30 @@ NS_IMETHODIMP
nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic, nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) const char16_t* aData)
{ {
if (sGCOnMemoryPressure && !nsCRT::strcmp(aTopic, "memory-pressure")) { if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
if(StringBeginsWith(nsDependentString(aData), if (sGCOnMemoryPressure) {
NS_LITERAL_STRING("low-memory-ongoing"))) { if(StringBeginsWith(nsDependentString(aData),
// Don't GC/CC if we are in an ongoing low-memory state since its very NS_LITERAL_STRING("low-memory-ongoing"))) {
// slow and it likely won't help us anyway. // Don't GC/CC if we are in an ongoing low-memory state since its very
return NS_OK; // slow and it likely won't help us anyway.
} return NS_OK;
nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE, }
nsJSContext::NonIncrementalGC,
nsJSContext::ShrinkingGC);
nsJSContext::CycleCollectNow();
if (NeedsGCAfterCC()) {
nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE, nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
nsJSContext::NonIncrementalGC, nsJSContext::NonIncrementalGC,
nsJSContext::ShrinkingGC); nsJSContext::ShrinkingGC);
nsJSContext::CycleCollectNow();
if (NeedsGCAfterCC()) {
nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
nsJSContext::NonIncrementalGC,
nsJSContext::ShrinkingGC);
}
} }
} else if (!nsCRT::strcmp(aTopic, "user-interaction-inactive")) {
if (sCompactOnUserInactive) {
nsJSContext::PokeShrinkingGC();
}
} else if (!nsCRT::strcmp(aTopic, "user-interaction-active")) {
nsJSContext::KillShrinkingGCTimer();
} else if (!nsCRT::strcmp(aTopic, "quit-application") || } else if (!nsCRT::strcmp(aTopic, "quit-application") ||
!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { !nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
sShuttingDown = true; sShuttingDown = true;
@ -1284,12 +1299,11 @@ nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
return; return;
} }
JSGCInvocationKind gckind = aShrinking == ShrinkingGC ? GC_SHRINK : GC_NORMAL;
JS::PrepareForFullGC(sRuntime); JS::PrepareForFullGC(sRuntime);
if (aIncremental == IncrementalGC) { if (aIncremental == IncrementalGC) {
MOZ_ASSERT(aShrinking == NonShrinkingGC); JS::StartIncrementalGC(sRuntime, gckind, aReason, aSliceMillis);
JS::StartIncrementalGC(sRuntime, GC_NORMAL, aReason, aSliceMillis);
} else { } else {
JSGCInvocationKind gckind = aShrinking == ShrinkingGC ? GC_SHRINK : GC_NORMAL;
JS::GCForReason(sRuntime, gckind, aReason); JS::GCForReason(sRuntime, gckind, aReason);
} }
} }
@ -1798,6 +1812,16 @@ ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
nsJSContext::ShrinkGCBuffersNow(); nsJSContext::ShrinkGCBuffersNow();
} }
// static
void
ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure)
{
nsJSContext::KillShrinkingGCTimer();
nsJSContext::GarbageCollectNow(JS::gcreason::USER_INACTIVE,
nsJSContext::IncrementalGC,
nsJSContext::ShrinkingGC);
}
static bool static bool
ShouldTriggerCC(uint32_t aSuspected) ShouldTriggerCC(uint32_t aSuspected)
{ {
@ -2030,6 +2054,26 @@ nsJSContext::PokeShrinkGCBuffers()
nsITimer::TYPE_ONE_SHOT); nsITimer::TYPE_ONE_SHOT);
} }
// static
void
nsJSContext::PokeShrinkingGC()
{
if (sShrinkingGCTimer || sShuttingDown) {
return;
}
CallCreateInstance("@mozilla.org/timer;1", &sShrinkingGCTimer);
if (!sShrinkingGCTimer) {
// Failed to create timer (probably because we're in XPCOM shutdown)
return;
}
sShrinkingGCTimer->InitWithFuncCallback(ShrinkingGCTimerFired, nullptr,
NS_SHRINKING_GC_DELAY,
nsITimer::TYPE_ONE_SHOT);
}
// static // static
void void
nsJSContext::MaybePokeCC() nsJSContext::MaybePokeCC()
@ -2091,6 +2135,16 @@ nsJSContext::KillShrinkGCBuffersTimer()
} }
} }
//static
void
nsJSContext::KillShrinkingGCTimer()
{
if (sShrinkingGCTimer) {
sShrinkingGCTimer->Cancel();
NS_RELEASE(sShrinkingGCTimer);
}
}
//static //static
void void
nsJSContext::KillCCTimer() nsJSContext::KillCCTimer()
@ -2282,7 +2336,7 @@ void
mozilla::dom::StartupJSEnvironment() mozilla::dom::StartupJSEnvironment()
{ {
// initialize all our statics, so that we can restart XPCOM // initialize all our statics, so that we can restart XPCOM
sGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr; sGCTimer = sShrinkingGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr;
sCCLockedOut = false; sCCLockedOut = false;
sCCLockedOutTime = 0; sCCLockedOutTime = 0;
sLastCCEndTime = TimeStamp(); sLastCCEndTime = TimeStamp();
@ -2687,8 +2741,14 @@ nsJSContext::EnsureStatics()
"javascript.options.gc_on_memory_pressure", "javascript.options.gc_on_memory_pressure",
true); true);
Preferences::AddBoolVarCache(&sCompactOnUserInactive,
"javascript.options.compact_on_user_inactive",
true);
nsIObserver* observer = new nsJSEnvironmentObserver(); nsIObserver* observer = new nsJSEnvironmentObserver();
obs->AddObserver(observer, "memory-pressure", false); obs->AddObserver(observer, "memory-pressure", false);
obs->AddObserver(observer, "user-interaction-inactive", false);
obs->AddObserver(observer, "user-interaction-active", false);
obs->AddObserver(observer, "quit-application", false); obs->AddObserver(observer, "quit-application", false);
obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);

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

@ -116,6 +116,9 @@ public:
static void PokeShrinkGCBuffers(); static void PokeShrinkGCBuffers();
static void KillShrinkGCBuffersTimer(); static void KillShrinkGCBuffersTimer();
static void PokeShrinkingGC();
static void KillShrinkingGCTimer();
static void MaybePokeCC(); static void MaybePokeCC();
static void KillCCTimer(); static void KillCCTimer();
static void KillICCTimer(); static void KillICCTimer();

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

@ -99,7 +99,8 @@ namespace JS {
D(REFRESH_FRAME) \ D(REFRESH_FRAME) \
D(FULL_GC_TIMER) \ D(FULL_GC_TIMER) \
D(SHUTDOWN_CC) \ D(SHUTDOWN_CC) \
D(FINISH_LARGE_EVALUATE) D(FINISH_LARGE_EVALUATE) \
D(USER_INACTIVE)
namespace gcreason { namespace gcreason {

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

@ -364,7 +364,9 @@ js::AssertSameCompartment(JSObject *objA, JSObject *objB)
JS_FRIEND_API(void) JS_FRIEND_API(void)
js::NotifyAnimationActivity(JSObject *obj) js::NotifyAnimationActivity(JSObject *obj)
{ {
obj->compartment()->lastAnimationTime = PRMJ_Now(); int64_t timeNow = PRMJ_Now();
obj->compartment()->lastAnimationTime = timeNow;
obj->runtimeFromMainThread()->lastAnimationTime = timeNow;
} }
JS_FRIEND_API(uint32_t) JS_FRIEND_API(uint32_t)

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

@ -1888,7 +1888,10 @@ ArenaLists::allocateFromArenaInner(JS::Zone *zone, ArenaHeader *aheader, AllocKi
bool bool
GCRuntime::shouldCompact() GCRuntime::shouldCompact()
{ {
return invocationKind == GC_SHRINK && isCompactingGCEnabled(); // Compact on shrinking GC if enabled, but skip compacting in incremental
// GCs if we are currently animating.
return invocationKind == GC_SHRINK && isCompactingGCEnabled() &&
(!isIncremental || rt->lastAnimationTime + PRMJ_USEC_PER_SEC < PRMJ_Now());
} }
void void

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

@ -212,7 +212,8 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
#endif #endif
largeAllocationFailureCallback(nullptr), largeAllocationFailureCallback(nullptr),
oomCallback(nullptr), oomCallback(nullptr),
debuggerMallocSizeOf(ReturnZeroSize) debuggerMallocSizeOf(ReturnZeroSize),
lastAnimationTime(0)
{ {
setGCStoreBufferPtr(&gc.storeBuffer); setGCStoreBufferPtr(&gc.storeBuffer);

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

@ -1417,6 +1417,9 @@ struct JSRuntime : public JS::shadow::Runtime,
* function to assess the size of malloc'd blocks of memory. * function to assess the size of malloc'd blocks of memory.
*/ */
mozilla::MallocSizeOf debuggerMallocSizeOf; mozilla::MallocSizeOf debuggerMallocSizeOf;
/* Last time at which an animation was played for this runtime. */
int64_t lastAnimationTime;
}; };
namespace js { namespace js {

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

@ -1081,6 +1081,7 @@ pref("javascript.options.mem.gc_compacting", true);
pref("javascript.options.mem.log", false); pref("javascript.options.mem.log", false);
pref("javascript.options.mem.notify", false); pref("javascript.options.mem.notify", false);
pref("javascript.options.gc_on_memory_pressure", true); pref("javascript.options.gc_on_memory_pressure", true);
pref("javascript.options.compact_on_user_inactive", true);
pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 1000); pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 1000);
pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 100); pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 100);