зеркало из https://github.com/mozilla/pjs.git
Memory-pressure based GC scheduler (506125, r=igor).
This commit is contained in:
Родитель
92b6bf0c5a
Коммит
41ac1aacfa
|
@ -858,18 +858,22 @@ PrintWinCodebase(nsGlobalWindow *win)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Don't GC for the first 10s (startup).
|
||||
PRIntervalTime startTime = -1;
|
||||
|
||||
static void
|
||||
MaybeGC(JSContext *cx)
|
||||
{
|
||||
size_t bytes = cx->runtime->gcBytes;
|
||||
size_t lastBytes = cx->runtime->gcLastBytes;
|
||||
if ((bytes > 8192 && bytes > lastBytes * 16)
|
||||
#ifdef DEBUG
|
||||
|| cx->runtime->gcZeal > 0
|
||||
#endif
|
||||
) {
|
||||
JS_GC(cx);
|
||||
if (startTime) {
|
||||
if (startTime == -1) {
|
||||
startTime = PR_IntervalNow();
|
||||
return;
|
||||
}
|
||||
if (PR_IntervalToMilliseconds(PR_IntervalNow() - startTime) < 10000)
|
||||
return;
|
||||
startTime = 0;
|
||||
}
|
||||
JS_MaybeGC(cx);
|
||||
}
|
||||
|
||||
static already_AddRefed<nsIPrompt>
|
||||
|
|
|
@ -1980,7 +1980,7 @@ case "$target" in
|
|||
DSO_LDOPTS=-SUBSYSTEM:WINDOWS
|
||||
CFLAGS="$CFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)"
|
||||
CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)"
|
||||
LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib"
|
||||
LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib psapi.lib"
|
||||
MOZ_DEBUG_FLAGS='-Zi'
|
||||
MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV'
|
||||
WARNINGS_AS_ERRORS='-WX'
|
||||
|
|
|
@ -2447,72 +2447,7 @@ JS_GC(JSContext *cx)
|
|||
JS_PUBLIC_API(void)
|
||||
JS_MaybeGC(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
uint32 bytes, lastBytes;
|
||||
|
||||
rt = cx->runtime;
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
if (rt->gcZeal > 0) {
|
||||
JS_GC(cx);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
bytes = rt->gcBytes;
|
||||
lastBytes = rt->gcLastBytes;
|
||||
|
||||
/*
|
||||
* We run the GC if we used all available free GC cells and had to
|
||||
* allocate extra 1/3 of GC arenas since the last run of GC, or if
|
||||
* we have malloc'd more bytes through JS_malloc than we were told
|
||||
* to allocate by JS_NewRuntime.
|
||||
*
|
||||
* The reason for
|
||||
* bytes > 4/3 lastBytes
|
||||
* condition is the following. Bug 312238 changed bytes and lastBytes
|
||||
* to mean the total amount of memory that the GC uses now and right
|
||||
* after the last GC.
|
||||
*
|
||||
* Before the bug the variables meant the size of allocated GC things
|
||||
* now and right after the last GC. That size did not include the
|
||||
* memory taken by free GC cells and the condition was
|
||||
* bytes > 3/2 lastBytes.
|
||||
* That is, we run the GC if we have half again as many bytes of
|
||||
* GC-things as the last time we GC'd. To be compatible we need to
|
||||
* express that condition through the new meaning of bytes and
|
||||
* lastBytes.
|
||||
*
|
||||
* We write the original condition as
|
||||
* B*(1-F) > 3/2 Bl*(1-Fl)
|
||||
* where B is the total memory size allocated by GC and F is the free
|
||||
* cell density currently and Sl and Fl are the size and the density
|
||||
* right after GC. The density by definition is memory taken by free
|
||||
* cells divided by total amount of memory. In other words, B and Bl
|
||||
* are bytes and lastBytes with the new meaning and B*(1-F) and
|
||||
* Bl*(1-Fl) are bytes and lastBytes with the original meaning.
|
||||
*
|
||||
* Our task is to exclude F and Fl from the last statement. According
|
||||
* to the stats from bug 331966 comment 23, Fl is about 10-25% for a
|
||||
* typical run of the browser. It means that the original condition
|
||||
* implied that we did not run GC unless we exhausted the pool of
|
||||
* free cells. Indeed if we still have free cells, then B == Bl since
|
||||
* we did not yet allocated any new arenas and the condition means
|
||||
* 1 - F > 3/2 (1-Fl) or 3/2Fl > 1/2 + F
|
||||
* That implies 3/2 Fl > 1/2 or Fl > 1/3. That cannot be fulfilled
|
||||
* for the state described by the stats. So we can write the original
|
||||
* condition as:
|
||||
* F == 0 && B > 3/2 Bl(1-Fl)
|
||||
* Again using the stats we see that Fl is about 11% when the browser
|
||||
* starts up and when we are far from hitting rt->gcMaxBytes. With
|
||||
* this F we have
|
||||
* F == 0 && B > 3/2 Bl(1-0.11)
|
||||
* or approximately F == 0 && B > 4/3 Bl.
|
||||
*/
|
||||
if ((bytes > 8192 && bytes > lastBytes + lastBytes / 3) ||
|
||||
rt->gcMallocBytes >= rt->gcMaxMallocBytes) {
|
||||
JS_GC(cx);
|
||||
}
|
||||
js_MaybeGC(cx);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSGCCallback)
|
||||
|
@ -2546,17 +2481,10 @@ JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value)
|
|||
case JSGC_MAX_BYTES:
|
||||
rt->gcMaxBytes = value;
|
||||
break;
|
||||
case JSGC_MAX_MALLOC_BYTES:
|
||||
rt->gcMaxMallocBytes = value;
|
||||
break;
|
||||
case JSGC_STACKPOOL_LIFESPAN:
|
||||
default:
|
||||
JS_ASSERT(key == JSGC_STACKPOOL_LIFESPAN);
|
||||
rt->gcEmptyArenaPoolLifespan = value;
|
||||
break;
|
||||
default:
|
||||
JS_ASSERT(key == JSGC_TRIGGER_FACTOR);
|
||||
JS_ASSERT(value >= 100);
|
||||
rt->setGCTriggerFactor(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2566,12 +2494,8 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key)
|
|||
switch (key) {
|
||||
case JSGC_MAX_BYTES:
|
||||
return rt->gcMaxBytes;
|
||||
case JSGC_MAX_MALLOC_BYTES:
|
||||
return rt->gcMaxMallocBytes;
|
||||
case JSGC_STACKPOOL_LIFESPAN:
|
||||
return rt->gcEmptyArenaPoolLifespan;
|
||||
case JSGC_TRIGGER_FACTOR:
|
||||
return rt->gcTriggerFactor;
|
||||
case JSGC_BYTES:
|
||||
return rt->gcBytes;
|
||||
default:
|
||||
|
|
|
@ -1259,30 +1259,17 @@ typedef enum JSGCParamKey {
|
|||
/* Maximum nominal heap before last ditch GC. */
|
||||
JSGC_MAX_BYTES = 0,
|
||||
|
||||
/* Number of JS_malloc bytes before last ditch GC. */
|
||||
JSGC_MAX_MALLOC_BYTES = 1,
|
||||
|
||||
/* Hoard stackPools for this long, in ms, default is 30 seconds. */
|
||||
JSGC_STACKPOOL_LIFESPAN = 2,
|
||||
|
||||
/*
|
||||
* The factor that defines when the GC is invoked. The factor is a
|
||||
* percent of the memory allocated by the GC after the last run of
|
||||
* the GC. When the current memory allocated by the GC is more than
|
||||
* this percent then the GC is invoked. The factor cannot be less
|
||||
* than 100 since the current memory allocated by the GC cannot be less
|
||||
* than the memory allocated after the last run of the GC.
|
||||
*/
|
||||
JSGC_TRIGGER_FACTOR = 3,
|
||||
JSGC_STACKPOOL_LIFESPAN = 1,
|
||||
|
||||
/* Amount of bytes allocated by the GC. */
|
||||
JSGC_BYTES = 4,
|
||||
JSGC_BYTES = 2,
|
||||
|
||||
/* Number of times when GC was invoked. */
|
||||
JSGC_NUMBER = 5,
|
||||
JSGC_NUMBER = 3,
|
||||
|
||||
/* Max size of the code cache in bytes. */
|
||||
JSGC_MAX_CODE_CACHE_BYTES = 6
|
||||
JSGC_MAX_CODE_CACHE_BYTES = 4
|
||||
} JSGCParamKey;
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
|
|
|
@ -1779,6 +1779,7 @@ js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked)
|
|||
if (!gcLocked)
|
||||
JS_LOCK_GC(rt);
|
||||
#endif
|
||||
|
||||
iter = NULL;
|
||||
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)))
|
||||
JS_TriggerOperationCallback(acx);
|
||||
|
|
|
@ -251,12 +251,6 @@ struct JSThreadData {
|
|||
JSEvalCacheMeter evalCacheMeter;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Thread-local version of JSRuntime.gcMallocBytes to avoid taking
|
||||
* locks on each JS_malloc.
|
||||
*/
|
||||
size_t gcMallocBytes;
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
/*
|
||||
* Deallocator task for this thread.
|
||||
|
@ -376,16 +370,14 @@ struct JSRuntime {
|
|||
JSDHashTable gcRootsHash;
|
||||
JSDHashTable *gcLocksHash;
|
||||
jsrefcount gcKeepAtoms;
|
||||
size_t gcBytes;
|
||||
size_t gcLastBytes;
|
||||
size_t gcMaxBytes;
|
||||
size_t gcMaxMallocBytes;
|
||||
uint32 gcEmptyArenaPoolLifespan;
|
||||
uint32 gcLevel;
|
||||
uint32 gcNumber;
|
||||
JSTracer *gcMarkingTracer;
|
||||
uint32 gcTriggerFactor;
|
||||
size_t gcTriggerBytes;
|
||||
size_t gcBytes;
|
||||
size_t gcMaxBytes;
|
||||
size_t gcLastRSS;
|
||||
size_t gcFreed;
|
||||
volatile JSBool gcIsNeeded;
|
||||
|
||||
/*
|
||||
|
@ -414,7 +406,6 @@ struct JSRuntime {
|
|||
#endif
|
||||
|
||||
JSGCCallback gcCallback;
|
||||
size_t gcMallocBytes;
|
||||
JSGCArenaInfo *gcUntracedArenaStackTop;
|
||||
#ifdef DEBUG
|
||||
size_t gcTraceLaterCount;
|
||||
|
@ -703,9 +694,6 @@ struct JSRuntime {
|
|||
char lastScriptFilename[1024];
|
||||
#endif
|
||||
|
||||
void setGCTriggerFactor(uint32 factor);
|
||||
void setGCLastBytes(size_t lastBytes);
|
||||
|
||||
inline void* malloc(size_t bytes) {
|
||||
return ::js_malloc(bytes);
|
||||
}
|
||||
|
@ -1077,15 +1065,15 @@ struct JSContext {
|
|||
#endif
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
inline void createDeallocatorTask() {
|
||||
JSThreadData* tls = JS_THREAD_DATA(this);
|
||||
inline void createDeallocatorTask(size_t *bytesp) {
|
||||
JSThreadData *tls = JS_THREAD_DATA(this);
|
||||
JS_ASSERT(!tls->deallocatorTask);
|
||||
if (runtime->deallocatorThread && !runtime->deallocatorThread->busy())
|
||||
tls->deallocatorTask = new JSFreePointerListTask();
|
||||
tls->deallocatorTask = new JSFreePointerListTask(bytesp);
|
||||
}
|
||||
|
||||
inline void submitDeallocatorTask() {
|
||||
JSThreadData* tls = JS_THREAD_DATA(this);
|
||||
JSThreadData *tls = JS_THREAD_DATA(this);
|
||||
if (tls->deallocatorTask) {
|
||||
runtime->deallocatorThread->schedule(tls->deallocatorTask);
|
||||
tls->deallocatorTask = NULL;
|
||||
|
@ -1093,46 +1081,32 @@ struct JSContext {
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Call this after succesful malloc of memory for GC-related things. */
|
||||
inline void updateMallocCounter(size_t nbytes) {
|
||||
size_t *pbytes, bytes;
|
||||
|
||||
pbytes = &JS_THREAD_DATA(this)->gcMallocBytes;
|
||||
bytes = *pbytes;
|
||||
*pbytes = (size_t(-1) - bytes <= nbytes) ? size_t(-1) : bytes + nbytes;
|
||||
}
|
||||
|
||||
inline void* malloc(size_t bytes) {
|
||||
inline void *malloc(size_t bytes) {
|
||||
JS_ASSERT(bytes != 0);
|
||||
void *p = runtime->malloc(bytes);
|
||||
if (!p) {
|
||||
JS_ReportOutOfMemory(this);
|
||||
return NULL;
|
||||
}
|
||||
updateMallocCounter(bytes);
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void* calloc(size_t bytes) {
|
||||
inline void *calloc(size_t bytes) {
|
||||
JS_ASSERT(bytes != 0);
|
||||
void *p = runtime->calloc(bytes);
|
||||
if (!p) {
|
||||
JS_ReportOutOfMemory(this);
|
||||
return NULL;
|
||||
}
|
||||
updateMallocCounter(bytes);
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void* realloc(void* p, size_t bytes) {
|
||||
void *orig = p;
|
||||
inline void *realloc(void* p, size_t bytes) {
|
||||
p = runtime->realloc(p, bytes);
|
||||
if (!p) {
|
||||
JS_ReportOutOfMemory(this);
|
||||
return NULL;
|
||||
}
|
||||
if (!orig)
|
||||
updateMallocCounter(bytes);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
|
267
js/src/jsgc.cpp
267
js/src/jsgc.cpp
|
@ -1298,24 +1298,13 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
|
|||
}
|
||||
rt->gcLocksHash = NULL; /* create lazily */
|
||||
|
||||
/*
|
||||
* Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
|
||||
* for default backward API compatibility.
|
||||
*/
|
||||
rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes;
|
||||
rt->gcMaxBytes = maxbytes;
|
||||
rt->gcEmptyArenaPoolLifespan = 30000;
|
||||
|
||||
/*
|
||||
* By default the trigger factor gets maximum possible value. This
|
||||
* means that GC will not be triggered by growth of GC memory (gcBytes).
|
||||
* Sample the current resident set size (RSS).
|
||||
*/
|
||||
rt->setGCTriggerFactor((uint32) -1);
|
||||
|
||||
/*
|
||||
* The assigned value prevents GC from running when GC memory is too low
|
||||
* (during JS engine start).
|
||||
*/
|
||||
rt->setGCLastBytes(8192);
|
||||
rt->gcLastRSS = js_GetRSS();
|
||||
|
||||
METER(memset(&rt->gcStats, 0, sizeof rt->gcStats));
|
||||
return JS_TRUE;
|
||||
|
@ -1724,42 +1713,6 @@ static struct GCHist {
|
|||
unsigned gchpos = 0;
|
||||
#endif
|
||||
|
||||
void
|
||||
JSRuntime::setGCTriggerFactor(uint32 factor)
|
||||
{
|
||||
JS_ASSERT(factor >= 100);
|
||||
|
||||
gcTriggerFactor = factor;
|
||||
setGCLastBytes(gcLastBytes);
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::setGCLastBytes(size_t lastBytes)
|
||||
{
|
||||
gcLastBytes = lastBytes;
|
||||
uint64 triggerBytes = uint64(lastBytes) * uint64(gcTriggerFactor / 100);
|
||||
if (triggerBytes != size_t(triggerBytes))
|
||||
triggerBytes = size_t(-1);
|
||||
gcTriggerBytes = size_t(triggerBytes);
|
||||
}
|
||||
|
||||
static JS_INLINE bool
|
||||
IsGCThresholdReached(JSRuntime *rt)
|
||||
{
|
||||
#ifdef JS_GC_ZEAL
|
||||
if (rt->gcZeal >= 1)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Since the initial value of the gcLastBytes parameter is not equal to
|
||||
* zero (see the js_InitGC function) the return value is false when
|
||||
* the gcBytes value is close to zero at the JS engine start.
|
||||
*/
|
||||
return rt->gcMallocBytes >= rt->gcMaxMallocBytes ||
|
||||
rt->gcBytes >= rt->gcTriggerBytes;
|
||||
}
|
||||
|
||||
template <class T> static JS_INLINE T*
|
||||
NewGCThing(JSContext *cx, uintN flags)
|
||||
{
|
||||
|
@ -1776,7 +1729,6 @@ NewGCThing(JSContext *cx, uintN flags)
|
|||
#endif
|
||||
#ifdef JS_THREADSAFE
|
||||
JSBool gcLocked;
|
||||
uintN localMallocBytes;
|
||||
JSGCThing **lastptr;
|
||||
JSGCThing *tmpthing;
|
||||
uint8 *tmpflagp;
|
||||
|
@ -1799,8 +1751,7 @@ NewGCThing(JSContext *cx, uintN flags)
|
|||
|
||||
JSGCThing *&freeList = cx->thread->gcFreeLists[flindex];
|
||||
thing = freeList;
|
||||
localMallocBytes = JS_THREAD_DATA(cx)->gcMallocBytes;
|
||||
if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) {
|
||||
if (thing) {
|
||||
flagp = thing->flagp;
|
||||
freeList = thing->next;
|
||||
METER(astats->localalloc++);
|
||||
|
@ -1810,14 +1761,6 @@ NewGCThing(JSContext *cx, uintN flags)
|
|||
JS_LOCK_GC(rt);
|
||||
gcLocked = JS_TRUE;
|
||||
|
||||
/* Transfer thread-local counter to global one. */
|
||||
if (localMallocBytes != 0) {
|
||||
JS_THREAD_DATA(cx)->gcMallocBytes = 0;
|
||||
if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes)
|
||||
rt->gcMallocBytes = rt->gcMaxMallocBytes;
|
||||
else
|
||||
rt->gcMallocBytes += localMallocBytes;
|
||||
}
|
||||
#endif
|
||||
JS_ASSERT(!rt->gcRunning);
|
||||
if (rt->gcRunning) {
|
||||
|
@ -1832,7 +1775,7 @@ NewGCThing(JSContext *cx, uintN flags)
|
|||
#endif
|
||||
|
||||
arenaList = &rt->gcArenaList[flindex];
|
||||
doGC = IsGCThresholdReached(rt);
|
||||
doGC = rt->gcIsNeeded;
|
||||
for (;;) {
|
||||
if (doGC
|
||||
#ifdef JS_TRACER
|
||||
|
@ -1861,13 +1804,10 @@ NewGCThing(JSContext *cx, uintN flags)
|
|||
#ifdef JS_THREADSAFE
|
||||
/*
|
||||
* Refill the local free list by taking several things from the
|
||||
* global free list unless the free list is already populated or
|
||||
* we are still at rt->gcMaxMallocBytes barrier. The former is
|
||||
* caused via allocating new things in gcCallback(cx, JSGC_END).
|
||||
* The latter happens when GC is canceled due to
|
||||
* gcCallback(cx, JSGC_BEGIN) returning false.
|
||||
* global free list unless the free list is already populated.
|
||||
* This is caused by allocating new things in gcCallback(cx, JSGC_END).
|
||||
*/
|
||||
if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes)
|
||||
if (freeList)
|
||||
break;
|
||||
|
||||
tmpthing = arenaList->freeList;
|
||||
|
@ -1937,7 +1877,7 @@ testReservedObjects:
|
|||
* arena. Prefer to order free things by ascending address in the
|
||||
* (unscientific) hope of better cache locality.
|
||||
*/
|
||||
if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes)
|
||||
if (freeList)
|
||||
break;
|
||||
lastptr = &freeList;
|
||||
maxFreeThings = thingsLimit - arenaList->lastCount;
|
||||
|
@ -2064,7 +2004,7 @@ RefillDoubleFreeList(JSContext *cx)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (IsGCThresholdReached(rt))
|
||||
if (rt->gcIsNeeded)
|
||||
goto do_gc;
|
||||
|
||||
/*
|
||||
|
@ -2251,45 +2191,6 @@ js_ReserveObjects(JSContext *cx, size_t nobjects)
|
|||
}
|
||||
#endif
|
||||
|
||||
JSBool
|
||||
js_AddAsGCBytes(JSContext *cx, size_t sz)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
|
||||
rt = cx->runtime;
|
||||
if (rt->gcBytes >= rt->gcMaxBytes ||
|
||||
sz > (size_t) (rt->gcMaxBytes - rt->gcBytes) ||
|
||||
IsGCThresholdReached(rt)) {
|
||||
if (JS_ON_TRACE(cx)) {
|
||||
/*
|
||||
* If we can't leave the trace, signal OOM condition, otherwise
|
||||
* exit from trace and proceed with GC.
|
||||
*/
|
||||
if (!js_CanLeaveTrace(cx)) {
|
||||
JS_UNLOCK_GC(rt);
|
||||
return JS_FALSE;
|
||||
}
|
||||
js_LeaveTrace(cx);
|
||||
}
|
||||
js_GC(cx, GC_LAST_DITCH);
|
||||
if (rt->gcBytes >= rt->gcMaxBytes ||
|
||||
sz > (size_t) (rt->gcMaxBytes - rt->gcBytes)) {
|
||||
JS_UNLOCK_GC(rt);
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
rt->gcBytes += (uint32) sz;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
js_RemoveAsGCBytes(JSRuntime *rt, size_t sz)
|
||||
{
|
||||
JS_ASSERT((size_t) rt->gcBytes >= sz);
|
||||
rt->gcBytes -= (uint32) sz;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shallow GC-things can be locked just by setting the GCF_LOCK bit, because
|
||||
* they have no descendants to mark during the GC. Currently the optimization
|
||||
|
@ -3498,9 +3399,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
|||
/* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */
|
||||
rt->gcIsNeeded = JS_FALSE;
|
||||
|
||||
/* Reset malloc counter. */
|
||||
rt->gcMallocBytes = 0;
|
||||
|
||||
#ifdef JS_DUMP_SCOPE_METERS
|
||||
{ extern void js_DumpScopeMeters(JSRuntime *rt);
|
||||
js_DumpScopeMeters(rt);
|
||||
|
@ -3561,7 +3459,14 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
|||
rt->gcMarkingTracer = NULL;
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
cx->createDeallocatorTask();
|
||||
/*
|
||||
* Deallocations occur in the background. The background thread will set
|
||||
* gcFreed once it is done. At the next GC we will substract the amount
|
||||
* of data we freed in the background from the previous GC cycle's
|
||||
* RSS sample.
|
||||
*/
|
||||
rt->gcFreed = 0;
|
||||
cx->createDeallocatorTask(&rt->gcFreed);
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -3742,7 +3647,16 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
|||
*/
|
||||
DestroyGCArenas(rt, emptyArenas);
|
||||
|
||||
/* Sample Resident Set Size (RSS). */
|
||||
rt->gcLastRSS = js_GetRSS();
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
/*
|
||||
* It is important that we sample rt->gcLastRSS before submitting the
|
||||
* deallocator task. This way we know that the RSS we sample ddoes not
|
||||
* contain the bytes we will free in the background, which we will
|
||||
* account for separately in rt->gcFreed.
|
||||
*/
|
||||
cx->submitDeallocatorTask();
|
||||
#endif
|
||||
|
||||
|
@ -3800,7 +3714,6 @@ out:
|
|||
goto restart;
|
||||
}
|
||||
|
||||
rt->setGCLastBytes(rt->gcBytes);
|
||||
done_running:
|
||||
rt->gcLevel = 0;
|
||||
rt->gcRunning = rt->gcRegenShapes = false;
|
||||
|
@ -3852,3 +3765,129 @@ out:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
js_MaybeGC(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
|
||||
rt = cx->runtime;
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
if (rt->gcZeal > 0) {
|
||||
JS_GC(cx);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t lastRSS = rt->gcLastRSS;
|
||||
size_t freed = rt->gcFreed;
|
||||
if (lastRSS > freed)
|
||||
lastRSS -= freed;
|
||||
if (js_GetRSS() > (lastRSS + lastRSS/16 + lastRSS/8)) // 18.75% growth?
|
||||
JS_GC(cx);
|
||||
}
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
void
|
||||
JSFreePointerListTask::add(void *ptr)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
memset(ptr, 0xcd, js_malloc_size(ptr));
|
||||
#endif
|
||||
*(void**)ptr = head;
|
||||
head = ptr;
|
||||
}
|
||||
|
||||
void
|
||||
JSFreePointerListTask::run()
|
||||
{
|
||||
size_t bytes = 0;
|
||||
void *ptr = head;
|
||||
while (ptr) {
|
||||
void* next = *(void **)ptr;
|
||||
bytes += js_malloc_size(ptr);
|
||||
js_free(ptr);
|
||||
ptr = next;
|
||||
}
|
||||
JS_ATOMIC_ADD(bytesp, bytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <mach/mach.h>
|
||||
#include <mach/task_info.h>
|
||||
size_t
|
||||
js_GetRSS()
|
||||
{
|
||||
task_t task;
|
||||
task_basic_info ti;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
task_for_pid(mach_task_self (), getpid(), &task);
|
||||
count = TASK_BASIC_INFO_COUNT;
|
||||
task_info(task, TASK_BASIC_INFO, (task_info_t)&ti, &count);
|
||||
return ti.resident_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WINCE
|
||||
#include "windows.h"
|
||||
|
||||
size_t
|
||||
js_GetRSS()
|
||||
{
|
||||
MEMORYSTATUS ms;
|
||||
GlobalMemoryStatus(&ms);
|
||||
return ms.dwTotalVirtual - ms.dwAvailVirtual;
|
||||
}
|
||||
#else
|
||||
#ifdef WIN32
|
||||
#include "windows.h"
|
||||
#include "psapi.h"
|
||||
size_t
|
||||
js_GetRSS()
|
||||
{
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
|
||||
return pmc.WorkingSetSize;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
static const char*
|
||||
skipFields(const char* p, unsigned n)
|
||||
{
|
||||
while (*++p)
|
||||
if ((*p == ' ') && (--n == 0))
|
||||
return p+1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int statfd = 0;
|
||||
|
||||
size_t
|
||||
js_GetRSS()
|
||||
{
|
||||
char buf[1024];
|
||||
if (!statfd) {
|
||||
sprintf(buf, "/proc/%d/stat", getpid());
|
||||
statfd = open(buf, O_RDONLY);
|
||||
}
|
||||
if (statfd == -1)
|
||||
return 0;
|
||||
lseek(statfd, 0, SEEK_SET);
|
||||
int n = read(statfd, buf, sizeof(buf)-1);
|
||||
buf[n] = 0;
|
||||
const char* p = strrchr(buf, ')') + 2;
|
||||
if (!p)
|
||||
return 0;
|
||||
p = skipFields(p, 20);
|
||||
if (!p)
|
||||
return 0;
|
||||
return atol(p);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -284,6 +284,9 @@ typedef enum JSGCInvocationKind {
|
|||
extern void
|
||||
js_GC(JSContext *cx, JSGCInvocationKind gckind);
|
||||
|
||||
extern void
|
||||
js_MaybeGC(JSContext *cx);
|
||||
|
||||
typedef struct JSGCArenaInfo JSGCArenaInfo;
|
||||
typedef struct JSGCArenaList JSGCArenaList;
|
||||
typedef struct JSGCChunkInfo JSGCChunkInfo;
|
||||
|
@ -326,41 +329,15 @@ struct JSWeakRoots {
|
|||
|
||||
#define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots)))
|
||||
|
||||
/*
|
||||
* Increase runtime->gcBytes by sz bytes to account for an allocation outside
|
||||
* the GC that will be freed only after the GC is run. The function may run
|
||||
* the last ditch GC to ensure that gcBytes does not exceed gcMaxBytes. It will
|
||||
* fail if the latter is not possible.
|
||||
*
|
||||
* This function requires that runtime->gcLock is held on entry. On successful
|
||||
* return the lock is still held and on failure it will be released with
|
||||
* the error reported.
|
||||
*/
|
||||
extern JSBool
|
||||
js_AddAsGCBytes(JSContext *cx, size_t sz);
|
||||
|
||||
extern void
|
||||
js_RemoveAsGCBytes(JSRuntime* rt, size_t sz);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
class JSFreePointerListTask : public JSBackgroundTask {
|
||||
void *head;
|
||||
size_t *bytesp;
|
||||
public:
|
||||
JSFreePointerListTask() : head(NULL) {}
|
||||
JSFreePointerListTask(size_t *bytesp) : head(NULL), bytesp(bytesp) {}
|
||||
|
||||
void add(void* ptr) {
|
||||
*(void**)ptr = head;
|
||||
head = ptr;
|
||||
}
|
||||
|
||||
void run() {
|
||||
void *ptr = head;
|
||||
while (ptr) {
|
||||
void *next = *(void **)ptr;
|
||||
js_free(ptr);
|
||||
ptr = next;
|
||||
}
|
||||
}
|
||||
void add(void* ptr);
|
||||
void run();
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -374,6 +351,9 @@ class JSFreePointerListTask : public JSBackgroundTask {
|
|||
extern void
|
||||
js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx);
|
||||
|
||||
extern size_t
|
||||
js_GetRSS();
|
||||
|
||||
#ifdef DEBUG_notme
|
||||
#define JS_GCMETER 1
|
||||
#endif
|
||||
|
|
|
@ -4989,11 +4989,6 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
|
|||
JS_ASSERT(ne->cursor == (jsword) length);
|
||||
if (allocated != 0) {
|
||||
JS_LOCK_GC(cx->runtime);
|
||||
if (!js_AddAsGCBytes(cx, allocated)) {
|
||||
/* js_AddAsGCBytes releases the GC lock on failures. */
|
||||
cx->free(ne);
|
||||
return JS_FALSE;
|
||||
}
|
||||
ne->next = cx->runtime->nativeEnumerators;
|
||||
cx->runtime->nativeEnumerators = ne;
|
||||
JS_ASSERT(((jsuword) ne & (jsuword) 1) == (jsuword) 0);
|
||||
|
@ -5082,7 +5077,6 @@ js_TraceNativeEnumerators(JSTracer *trc)
|
|||
js_TraceId(trc, *cursor);
|
||||
} while (++cursor != end);
|
||||
} else if (doGC) {
|
||||
js_RemoveAsGCBytes(rt, NativeEnumeratorSize(ne->length));
|
||||
*nep = ne->next;
|
||||
trc->context->free(ne);
|
||||
continue;
|
||||
|
|
|
@ -172,7 +172,6 @@ JSScope::createTable(JSContext *cx, bool report)
|
|||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
cx->updateMallocCounter(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
|
||||
|
||||
hashShift = JS_DHASH_BITS - sizeLog2;
|
||||
for (sprop = lastProp; sprop; sprop = sprop->parent) {
|
||||
|
@ -411,9 +410,6 @@ JSScope::changeTable(JSContext *cx, int change)
|
|||
oldtable = table;
|
||||
table = newtable;
|
||||
|
||||
/* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */
|
||||
cx->runtime->gcMallocBytes += nbytes;
|
||||
|
||||
/* Copy only live entries, leaving removed and free ones behind. */
|
||||
for (oldspp = oldtable; oldsize != 0; oldspp++) {
|
||||
sprop = SPROP_FETCH(oldspp);
|
||||
|
|
|
@ -202,6 +202,26 @@ static JS_INLINE void js_free(void* p) {
|
|||
free(p);
|
||||
}
|
||||
|
||||
#ifdef XP_UNIX
|
||||
#ifdef __APPLE__
|
||||
#include <malloc/malloc.h>
|
||||
static JS_INLINE size_t js_malloc_size(void* p) {
|
||||
return malloc_size(p);
|
||||
}
|
||||
#else
|
||||
#include <malloc.h>
|
||||
static JS_INLINE size_t js_malloc_size(void* p) {
|
||||
return malloc_usable_size(p);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
#include <malloc.h>
|
||||
static JS_INLINE size_t js_malloc_size(void* p) {
|
||||
return _msize(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
#endif /* jsutil_h___ */
|
||||
|
|
|
@ -308,6 +308,8 @@ GetContextData(JSContext *cx)
|
|||
static JSBool
|
||||
ShellOperationCallback(JSContext *cx)
|
||||
{
|
||||
JS_MaybeGC(cx);
|
||||
|
||||
if (!gCanceled)
|
||||
return JS_TRUE;
|
||||
|
||||
|
@ -1117,16 +1119,12 @@ GCParameter(JSContext *cx, uintN argc, jsval *vp)
|
|||
return JS_FALSE;
|
||||
if (strcmp(paramName, "maxBytes") == 0) {
|
||||
param = JSGC_MAX_BYTES;
|
||||
} else if (strcmp(paramName, "maxMallocBytes") == 0) {
|
||||
param = JSGC_MAX_MALLOC_BYTES;
|
||||
} else if (strcmp(paramName, "gcStackpoolLifespan") == 0) {
|
||||
param = JSGC_STACKPOOL_LIFESPAN;
|
||||
} else if (strcmp(paramName, "gcBytes") == 0) {
|
||||
param = JSGC_BYTES;
|
||||
} else if (strcmp(paramName, "gcNumber") == 0) {
|
||||
param = JSGC_NUMBER;
|
||||
} else if (strcmp(paramName, "gcTriggerFactor") == 0) {
|
||||
param = JSGC_TRIGGER_FACTOR;
|
||||
} else {
|
||||
JS_ReportError(cx,
|
||||
"the first argument argument must be maxBytes, "
|
||||
|
@ -1153,11 +1151,6 @@ GCParameter(JSContext *cx, uintN argc, jsval *vp)
|
|||
"with non-zero value");
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (param == JSGC_TRIGGER_FACTOR && value < 100) {
|
||||
JS_ReportError(cx,
|
||||
"the gcTriggerFactor value must be >= 100");
|
||||
return JS_FALSE;
|
||||
}
|
||||
JS_SetGCParameter(cx->runtime, param, value);
|
||||
*vp = JSVAL_VOID;
|
||||
return JS_TRUE;
|
||||
|
|
|
@ -6397,7 +6397,11 @@ free(void *ptr)
|
|||
*/
|
||||
|
||||
size_t
|
||||
#ifdef __linux__
|
||||
malloc_usable_size(void *ptr)
|
||||
#else
|
||||
malloc_usable_size(const void *ptr)
|
||||
#endif
|
||||
{
|
||||
|
||||
#ifdef MALLOC_VALIDATE
|
||||
|
|
|
@ -92,7 +92,11 @@ void free(void *ptr);
|
|||
|
||||
int posix_memalign(void **memptr, size_t alignment, size_t size);
|
||||
void *memalign(size_t alignment, size_t size);
|
||||
#ifdef __linux
|
||||
size_t malloc_usable_size(void *ptr);
|
||||
#else
|
||||
size_t malloc_usable_size(const void *ptr);
|
||||
#endif
|
||||
void jemalloc_stats(jemalloc_stats_t *stats);
|
||||
|
||||
/* The x*() functions never return NULL. */
|
||||
|
|
Загрузка…
Ссылка в новой задаче