Bug 656120 - Change MaybeGC trigger. r=igor

This commit is contained in:
Gregor Wagner 2011-06-20 14:44:26 -07:00
Родитель d3a19207ce
Коммит 35b03ae9b3
5 изменённых файлов: 55 добавлений и 21 удалений

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

@ -684,7 +684,7 @@ JSRuntime::init(uint32 maxbytes)
return false; return false;
} }
atomsCompartment->setGCLastBytes(8192); atomsCompartment->setGCLastBytes(8192, GC_NORMAL);
if (!js_InitAtomState(this)) if (!js_InitAtomState(this))
return false; return false;

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

@ -422,6 +422,8 @@ struct JSRuntime {
uint32 gcEmptyArenaPoolLifespan; uint32 gcEmptyArenaPoolLifespan;
uint32 gcNumber; uint32 gcNumber;
js::GCMarker *gcMarkingTracer; js::GCMarker *gcMarkingTracer;
bool gcChunkAllocationSinceLastGC;
int64 gcNextFullGCTime;
int64 gcJitReleaseTime; int64 gcJitReleaseTime;
JSGCMode gcMode; JSGCMode gcMode;
volatile bool gcIsNeeded; volatile bool gcIsNeeded;
@ -793,7 +795,7 @@ struct JSRuntime {
bool init(uint32 maxbytes); bool init(uint32 maxbytes);
void setGCLastBytes(size_t lastBytes); void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind);
void reduceGCTriggerBytes(uint32 amount); void reduceGCTriggerBytes(uint32 amount);
/* /*

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

@ -493,7 +493,7 @@ struct JS_FRIEND_API(JSCompartment) {
void finalizeShapeArenaLists(JSContext *cx); void finalizeShapeArenaLists(JSContext *cx);
bool arenaListsAreEmpty(); bool arenaListsAreEmpty();
void setGCLastBytes(size_t lastBytes); void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind);
void reduceGCTriggerBytes(uint32 amount); void reduceGCTriggerBytes(uint32 amount);
js::DtoaCache dtoaCache; js::DtoaCache dtoaCache;

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

@ -514,11 +514,12 @@ PickChunk(JSContext *cx)
chunk->init(rt); chunk->init(rt);
cx->compartment->chunk = chunk; cx->compartment->chunk = chunk;
rt->gcChunkAllocationSinceLastGC = true;
return chunk; return chunk;
} }
static void static void
ExpireGCChunks(JSRuntime *rt) ExpireGCChunks(JSRuntime *rt, JSGCInvocationKind gckind)
{ {
static const size_t MaxAge = 3; static const size_t MaxAge = 3;
@ -530,7 +531,7 @@ ExpireGCChunks(JSRuntime *rt)
Chunk *chunk = e.front(); Chunk *chunk = e.front();
JS_ASSERT(chunk->info.runtime == rt); JS_ASSERT(chunk->info.runtime == rt);
if (chunk->unused()) { if (chunk->unused()) {
if (chunk->info.age++ > MaxAge) { if (gckind == GC_SHRINK || chunk->info.age++ > MaxAge) {
e.removeFront(); e.removeFront();
ReleaseGCChunk(rt, chunk); ReleaseGCChunk(rt, chunk);
continue; continue;
@ -614,7 +615,7 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
* The assigned value prevents GC from running when GC memory is too low * The assigned value prevents GC from running when GC memory is too low
* (during JS engine start). * (during JS engine start).
*/ */
rt->setGCLastBytes(8192); rt->setGCLastBytes(8192, GC_NORMAL);
rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_EIGHTH_LIFETIME; rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_EIGHTH_LIFETIME;
@ -1092,34 +1093,38 @@ js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data)
} }
void void
JSRuntime::setGCLastBytes(size_t lastBytes) JSRuntime::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
{ {
gcLastBytes = lastBytes; gcLastBytes = lastBytes;
float trigger = float(Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER)) * GC_HEAP_GROWTH_FACTOR;
size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER);
float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
gcTriggerBytes = size_t(Min(float(gcMaxBytes), trigger)); gcTriggerBytes = size_t(Min(float(gcMaxBytes), trigger));
} }
void void
JSRuntime::reduceGCTriggerBytes(uint32 amount) { JSRuntime::reduceGCTriggerBytes(uint32 amount) {
JS_ASSERT(amount > 0); JS_ASSERT(amount > 0);
JS_ASSERT((gcTriggerBytes - amount) > 0); JS_ASSERT(gcTriggerBytes - amount >= 0);
if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR) if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR)
return; return;
gcTriggerBytes -= amount; gcTriggerBytes -= amount;
} }
void void
JSCompartment::setGCLastBytes(size_t lastBytes) JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
{ {
gcLastBytes = lastBytes; gcLastBytes = lastBytes;
float trigger = float(Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER)) * GC_HEAP_GROWTH_FACTOR;
size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER);
float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
gcTriggerBytes = size_t(Min(float(rt->gcMaxBytes), trigger)); gcTriggerBytes = size_t(Min(float(rt->gcMaxBytes), trigger));
} }
void void
JSCompartment::reduceGCTriggerBytes(uint32 amount) { JSCompartment::reduceGCTriggerBytes(uint32 amount) {
JS_ASSERT(amount > 0); JS_ASSERT(amount > 0);
JS_ASSERT((gcTriggerBytes - amount) > 0); JS_ASSERT(gcTriggerBytes - amount >= 0);
if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR) if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR)
return; return;
gcTriggerBytes -= amount; gcTriggerBytes -= amount;
@ -1960,6 +1965,21 @@ MaybeGC(JSContext *cx)
if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4)) { if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4)) {
GCREASON(MAYBEGC); GCREASON(MAYBEGC);
js_GC(cx, (rt->gcMode == JSGC_MODE_COMPARTMENT) ? comp : NULL, GC_NORMAL); js_GC(cx, (rt->gcMode == JSGC_MODE_COMPARTMENT) ? comp : NULL, GC_NORMAL);
return;
}
/*
* On 32 bit setting gcNextFullGCTime below is not atomic and a race condition
* could trigger an GC. We tolerate this.
*/
int64 now = PRMJ_Now();
if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) {
if (rt->gcChunkAllocationSinceLastGC || rt->gcChunksWaitingToExpire) {
GCREASON(MAYBEGC);
js_GC(cx, NULL, GC_SHRINK);
} else {
rt->gcNextFullGCTime = now + GC_IDLE_FULL_SPAN;
}
} }
} }
@ -2094,10 +2114,11 @@ GCHelperThread::threadLoop(JSRuntime *rt)
} }
void void
GCHelperThread::startBackgroundSweep(JSRuntime *rt) GCHelperThread::startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind)
{ {
/* The caller takes the GC lock. */ /* The caller takes the GC lock. */
JS_ASSERT(!sweeping); JS_ASSERT(!sweeping);
lastGCKind = gckind;
sweeping = true; sweeping = true;
PR_NotifyCondVar(wakeup); PR_NotifyCondVar(wakeup);
} }
@ -2138,7 +2159,7 @@ GCHelperThread::doSweep()
for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i) for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i)
ArenaList::backgroundFinalize(cx, *i); ArenaList::backgroundFinalize(cx, *i);
finalizeVector.resize(0); finalizeVector.resize(0);
ExpireGCChunks(cx->runtime); ExpireGCChunks(cx->runtime, lastGCKind);
cx = NULL; cx = NULL;
if (freeCursor) { if (freeCursor) {
@ -2405,7 +2426,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
* use IsAboutToBeFinalized(). * use IsAboutToBeFinalized().
* This is done on the GCHelperThread if JS_THREADSAFE is defined. * This is done on the GCHelperThread if JS_THREADSAFE is defined.
*/ */
ExpireGCChunks(rt); ExpireGCChunks(rt, gckind);
#endif #endif
GCTIMESTAMP(sweepDestroyEnd); GCTIMESTAMP(sweepDestroyEnd);
@ -2661,7 +2682,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_P
if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
cx->gcBackgroundFree = NULL; cx->gcBackgroundFree = NULL;
rt->gcHelperThread.startBackgroundSweep(rt); rt->gcHelperThread.startBackgroundSweep(rt, gckind);
} else { } else {
JS_ASSERT(!cx->gcBackgroundFree); JS_ASSERT(!cx->gcBackgroundFree);
} }
@ -2669,12 +2690,12 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_P
rt->gcMarkAndSweep = false; rt->gcMarkAndSweep = false;
rt->gcRegenShapes = false; rt->gcRegenShapes = false;
rt->setGCLastBytes(rt->gcBytes); rt->setGCLastBytes(rt->gcBytes, gckind);
rt->gcCurrentCompartment = NULL; rt->gcCurrentCompartment = NULL;
rt->gcWeakMapList = NULL; rt->gcWeakMapList = NULL;
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
(*c)->setGCLastBytes((*c)->gcBytes); (*c)->setGCLastBytes((*c)->gcBytes, gckind);
} }
void void
@ -2731,6 +2752,10 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
* stop creating garbage. * stop creating garbage.
*/ */
} while (gckind == GC_LAST_CONTEXT && rt->gcPoke); } while (gckind == GC_LAST_CONTEXT && rt->gcPoke);
rt->gcNextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
rt->gcChunkAllocationSinceLastGC = false;
#ifdef JS_GCMETER #ifdef JS_GCMETER
js_DumpGCStats(cx->runtime, stderr); js_DumpGCStats(cx->runtime, stderr);
#endif #endif
@ -2857,7 +2882,7 @@ NewCompartment(JSContext *cx, JSPrincipals *principals)
JSPRINCIPALS_HOLD(cx, principals); JSPRINCIPALS_HOLD(cx, principals);
} }
compartment->setGCLastBytes(8192); compartment->setGCLastBytes(8192, GC_NORMAL);
/* /*
* Before reporting the OOM condition, |lock| needs to be cleaned up, * Before reporting the OOM condition, |lock| needs to be cleaned up,

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

@ -636,6 +636,9 @@ const size_t GC_ARENA_ALLOCATION_TRIGGER = 30 * js::GC_CHUNK_SIZE;
*/ */
const float GC_HEAP_GROWTH_FACTOR = 3.0f; const float GC_HEAP_GROWTH_FACTOR = 3.0f;
/* Perform a Full GC every 20 seconds if MaybeGC is called */
static const int64 GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
static inline size_t static inline size_t
GetFinalizableTraceKind(size_t thingKind) GetFinalizableTraceKind(size_t thingKind)
{ {
@ -1048,7 +1051,10 @@ typedef enum JSGCInvocationKind {
* Called from js_DestroyContext for last JSContext in a JSRuntime, when * Called from js_DestroyContext for last JSContext in a JSRuntime, when
* it is imperative that rt->gcPoke gets cleared early in js_GC. * it is imperative that rt->gcPoke gets cleared early in js_GC.
*/ */
GC_LAST_CONTEXT = 1 GC_LAST_CONTEXT = 1,
/* Minimize GC triggers and release empty GC chunks right away. */
GC_SHRINK = 2
} JSGCInvocationKind; } JSGCInvocationKind;
/* Pass NULL for |comp| to get a full GC. */ /* Pass NULL for |comp| to get a full GC. */
@ -1098,6 +1104,7 @@ class GCHelperThread {
PRCondVar* wakeup; PRCondVar* wakeup;
PRCondVar* sweepingDone; PRCondVar* sweepingDone;
bool shutdown; bool shutdown;
JSGCInvocationKind lastGCKind;
Vector<void **, 16, js::SystemAllocPolicy> freeVector; Vector<void **, 16, js::SystemAllocPolicy> freeVector;
void **freeCursor; void **freeCursor;
@ -1137,7 +1144,7 @@ class GCHelperThread {
void finish(JSRuntime *rt); void finish(JSRuntime *rt);
/* Must be called with GC lock taken. */ /* Must be called with GC lock taken. */
void startBackgroundSweep(JSRuntime *rt); void startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind);
void waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked = true); void waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked = true);