From a2f58bb615a6185f10c1da617eff47fbe0cc3cc9 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sat, 19 Feb 2011 22:59:49 -0800 Subject: [PATCH] When idle the GC holds on to unused chunks indefinitely (bug 631733, r=brendan, a=blocker). (relanding in a CLOSED TREE) --- dom/base/nsJSEnvironment.cpp | 6 ++++++ js/src/jsapi.cpp | 2 ++ js/src/jsapi.h | 5 ++++- js/src/jscntxt.h | 1 + js/src/jsgc.cpp | 22 ++++++++++------------ js/src/jsgc.h | 2 -- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index e89f9e39f417..f19e7c9eb780 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3480,6 +3480,12 @@ DOMGCCallback(JSContext *cx, JSGCStatus status) nsJSContext::PokeCC(); } } + + // If we didn't end up scheduling a GC, and there are unused + // chunks waiting to expire, make sure we will GC again soon. + if (!sGCTimer && JS_GetGCParameter(cx->runtime, JSGC_UNUSED_CHUNKS) > 0) { + nsJSContext::PokeGC(); + } } JSBool result = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ca3acefdaf18..629ef34a7d58 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2644,6 +2644,8 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) return rt->gcBytes; case JSGC_MODE: return uint32(rt->gcMode); + case JSGC_UNUSED_CHUNKS: + return uint32(rt->gcChunksWaitingToExpire); default: JS_ASSERT(key == JSGC_NUMBER); return rt->gcNumber; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index efa1e556f9c1..a598b04ad233 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1785,7 +1785,10 @@ typedef enum JSGCParamKey { JSGC_MAX_CODE_CACHE_BYTES = 6, /* Select GC mode. */ - JSGC_MODE = 7 + JSGC_MODE = 7, + + /* Number of GC chunks waiting to expire. */ + JSGC_UNUSED_CHUNKS = 8 } JSGCParamKey; typedef enum JSGCMode { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 17a1567dc6ff..4ff77fa42f81 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1047,6 +1047,7 @@ struct JSRuntime { size_t gcLastBytes; size_t gcMaxBytes; size_t gcMaxMallocBytes; + size_t gcChunksWaitingToExpire; uint32 gcEmptyArenaPoolLifespan; uint32 gcNumber; js::GCMarker *gcMarkingTracer; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 9ef631349d79..18c962cc36c8 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -360,14 +360,6 @@ Chunk::releaseArena(Arena *arena) info.age = 0; } -bool -Chunk::expire() -{ - if (!unused()) - return false; - return info.age++ > MaxAge; -} - JSRuntime * Chunk::getRuntime() { @@ -456,16 +448,22 @@ PickChunk(JSRuntime *rt) static void ExpireGCChunks(JSRuntime *rt) { + static const size_t MaxAge = 3; + /* Remove unused chunks. */ AutoLockGC lock(rt); + rt->gcChunksWaitingToExpire = 0; for (GCChunkSet::Enum e(rt->gcChunkSet); !e.empty(); e.popFront()) { Chunk *chunk = e.front(); JS_ASSERT(chunk->info.runtime == rt); - if (chunk->expire()) { - e.removeFront(); - ReleaseGCChunk(rt, chunk); - continue; + if (chunk->unused()) { + if (chunk->info.age++ > MaxAge) { + e.removeFront(); + ReleaseGCChunk(rt, chunk); + continue; + } + rt->gcChunksWaitingToExpire++; } } } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 6379023ac6f6..bb666e617e55 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -340,7 +340,6 @@ struct Chunk { sizeof(MarkingDelay); static const size_t ArenasPerChunk = (GC_CHUNK_SIZE - sizeof(ChunkInfo)) / BytesPerArena; - static const size_t MaxAge = 3; Arena arenas[ArenasPerChunk]; ArenaBitmap bitmaps[ArenasPerChunk]; @@ -362,7 +361,6 @@ struct Chunk { void releaseArena(Arena *a); JSRuntime *getRuntime(); - bool expire(); }; JS_STATIC_ASSERT(sizeof(Chunk) <= GC_CHUNK_SIZE); JS_STATIC_ASSERT(sizeof(Chunk) + Chunk::BytesPerArena > GC_CHUNK_SIZE);