This commit is contained in:
Andreas Gal 2009-08-25 14:43:09 -07:00
commit 723887cd04
15 changed files with 335 additions and 316 deletions

View File

@ -858,22 +858,18 @@ PrintWinCodebase(nsGlobalWindow *win)
}
#endif
// Don't GC for the first 10s (startup).
PRIntervalTime startTime = -1;
static void
MaybeGC(JSContext *cx)
{
if (startTime) {
if (startTime == -1) {
startTime = PR_IntervalNow();
return;
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 (PR_IntervalToMilliseconds(PR_IntervalNow() - startTime) < 10000)
return;
startTime = 0;
}
JS_MaybeGC(cx);
}
static already_AddRefed<nsIPrompt>
@ -3883,10 +3879,30 @@ SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
{
PRInt32 highwatermark = nsContentUtils::GetIntPref(aPrefName, 32);
if (highwatermark >= 32) {
// There are two options of memory usage in tracemonkey. One is
// to use malloc() and the other is to use memory for GC. (E.g.
// js_NewGCThing()/RefillDoubleFreeList()).
// Let's limit the high water mark for the first one to 32MB,
// and second one to 0xffffffff.
JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_MALLOC_BYTES,
32L * 1024L * 1024L);
JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES,
(highwatermark >= 32)
? 0xffffffff
: highwatermark * 1024L * 1024L);
0xffffffff);
} else {
JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_MALLOC_BYTES,
highwatermark * 1024L * 1024L);
JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES,
highwatermark * 1024L * 1024L);
}
return 0;
}
static int
SetMemoryGCFrequencyPrefChangedCallback(const char* aPrefName, void* aClosure)
{
PRInt32 triggerFactor = nsContentUtils::GetIntPref(aPrefName, 1600);
JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_TRIGGER_FACTOR, triggerFactor);
return 0;
}
@ -3979,6 +3995,12 @@ nsJSRuntime::Init()
SetMemoryHighWaterMarkPrefChangedCallback("javascript.options.mem.high_water_mark",
nsnull);
nsContentUtils::RegisterPrefCallback("javascript.options.mem.gc_frequency",
SetMemoryGCFrequencyPrefChangedCallback,
nsnull);
SetMemoryGCFrequencyPrefChangedCallback("javascript.options.mem.gc_frequency",
nsnull);
nsCOMPtr<nsIObserverService> obs =
do_GetService("@mozilla.org/observer-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -1952,7 +1952,7 @@ case "$target" in
_DEFINES_CXXFLAGS='-FI $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT'
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 psapi.lib"
LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib"
MOZ_DEBUG_FLAGS='-Zi'
MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV'
WARNINGS_AS_ERRORS='-WX'

View File

@ -2452,7 +2452,72 @@ JS_GC(JSContext *cx)
JS_PUBLIC_API(void)
JS_MaybeGC(JSContext *cx)
{
js_MaybeGC(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_PUBLIC_API(JSGCCallback)
@ -2486,10 +2551,17 @@ JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value)
case JSGC_MAX_BYTES:
rt->gcMaxBytes = value;
break;
default:
JS_ASSERT(key == JSGC_STACKPOOL_LIFESPAN);
case JSGC_MAX_MALLOC_BYTES:
rt->gcMaxMallocBytes = value;
break;
case JSGC_STACKPOOL_LIFESPAN:
rt->gcEmptyArenaPoolLifespan = value;
break;
default:
JS_ASSERT(key == JSGC_TRIGGER_FACTOR);
JS_ASSERT(value >= 100);
rt->setGCTriggerFactor(value);
return;
}
}
@ -2499,8 +2571,12 @@ 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:

View File

@ -1271,17 +1271,30 @@ 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 = 1,
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,
/* Amount of bytes allocated by the GC. */
JSGC_BYTES = 2,
JSGC_BYTES = 4,
/* Number of times when GC was invoked. */
JSGC_NUMBER = 3,
JSGC_NUMBER = 5,
/* Max size of the code cache in bytes. */
JSGC_MAX_CODE_CACHE_BYTES = 4
JSGC_MAX_CODE_CACHE_BYTES = 6
} JSGCParamKey;
extern JS_PUBLIC_API(void)

View File

@ -1799,7 +1799,6 @@ 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);

View File

@ -280,6 +280,12 @@ 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.
@ -402,14 +408,16 @@ 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;
size_t gcBytes;
size_t gcMaxBytes;
size_t gcLastRSS;
size_t gcFreed;
uint32 gcTriggerFactor;
size_t gcTriggerBytes;
volatile JSBool gcIsNeeded;
volatile JSBool gcFlushCodeCaches;
@ -439,6 +447,7 @@ struct JSRuntime {
#endif
JSGCCallback gcCallback;
size_t gcMallocBytes;
JSGCArenaInfo *gcUntracedArenaStackTop;
#ifdef DEBUG
size_t gcTraceLaterCount;
@ -719,6 +728,9 @@ 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);
}
@ -1078,11 +1090,11 @@ struct JSContext {
#endif
#ifdef JS_THREADSAFE
inline void createDeallocatorTask(size_t *bytesp) {
inline void createDeallocatorTask() {
JSThreadData* tls = JS_THREAD_DATA(this);
JS_ASSERT(!tls->deallocatorTask);
if (runtime->deallocatorThread && !runtime->deallocatorThread->busy())
tls->deallocatorTask = new JSFreePointerListTask(bytesp);
tls->deallocatorTask = new JSFreePointerListTask();
}
inline void submitDeallocatorTask() {
@ -1094,6 +1106,15 @@ 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) {
JS_ASSERT(bytes != 0);
void *p = runtime->malloc(bytes);
@ -1101,6 +1122,7 @@ struct JSContext {
JS_ReportOutOfMemory(this);
return NULL;
}
updateMallocCounter(bytes);
return p;
}
@ -1111,15 +1133,19 @@ struct JSContext {
JS_ReportOutOfMemory(this);
return NULL;
}
updateMallocCounter(bytes);
return p;
}
inline void* realloc(void* p, size_t bytes) {
void *orig = p;
p = runtime->realloc(p, bytes);
if (!p) {
JS_ReportOutOfMemory(this);
return NULL;
}
if (!orig)
updateMallocCounter(bytes);
return p;
}

View File

@ -1298,13 +1298,24 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
}
rt->gcLocksHash = NULL; /* create lazily */
rt->gcMaxBytes = maxbytes;
/*
* Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
* for default backward API compatibility.
*/
rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes;
rt->gcEmptyArenaPoolLifespan = 30000;
/*
* Sample the current resident set size (RSS).
* By default the trigger factor gets maximum possible value. This
* means that GC will not be triggered by growth of GC memory (gcBytes).
*/
rt->gcLastRSS = js_GetRSS();
rt->setGCTriggerFactor((uint32) -1);
/*
* The assigned value prevents GC from running when GC memory is too low
* (during JS engine start).
*/
rt->setGCLastBytes(8192);
METER(memset(&rt->gcStats, 0, sizeof rt->gcStats));
return JS_TRUE;
@ -1713,6 +1724,42 @@ 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)
{
@ -1729,6 +1776,7 @@ NewGCThing(JSContext *cx, uintN flags)
#endif
#ifdef JS_THREADSAFE
JSBool gcLocked;
uintN localMallocBytes;
JSGCThing **lastptr;
JSGCThing *tmpthing;
uint8 *tmpflagp;
@ -1751,7 +1799,8 @@ NewGCThing(JSContext *cx, uintN flags)
JSGCThing *&freeList = cx->thread->gcFreeLists[flindex];
thing = freeList;
if (thing) {
localMallocBytes = JS_THREAD_DATA(cx)->gcMallocBytes;
if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) {
flagp = thing->flagp;
freeList = thing->next;
METER(astats->localalloc++);
@ -1761,6 +1810,14 @@ 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) {
@ -1775,7 +1832,7 @@ NewGCThing(JSContext *cx, uintN flags)
#endif
arenaList = &rt->gcArenaList[flindex];
doGC = rt->gcIsNeeded;
doGC = IsGCThresholdReached(rt);
for (;;) {
if (doGC
#ifdef JS_TRACER
@ -1804,10 +1861,13 @@ 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.
* This is caused by allocating new things in gcCallback(cx, JSGC_END).
* 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.
*/
if (freeList)
if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes)
break;
tmpthing = arenaList->freeList;
@ -1877,7 +1937,7 @@ testReservedObjects:
* arena. Prefer to order free things by ascending address in the
* (unscientific) hope of better cache locality.
*/
if (freeList)
if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes)
break;
lastptr = &freeList;
maxFreeThings = thingsLimit - arenaList->lastCount;
@ -2004,7 +2064,7 @@ RefillDoubleFreeList(JSContext *cx)
return NULL;
}
if (rt->gcIsNeeded)
if (IsGCThresholdReached(rt))
goto do_gc;
/*
@ -2191,6 +2251,45 @@ 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
@ -3392,6 +3491,9 @@ 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);
@ -3452,14 +3554,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
rt->gcMarkingTracer = NULL;
#ifdef JS_THREADSAFE
/*
* 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);
cx->createDeallocatorTask();
#endif
/*
@ -3640,16 +3735,7 @@ 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
@ -3707,6 +3793,7 @@ out:
goto restart;
}
rt->setGCLastBytes(rt->gcBytes);
done_running:
rt->gcLevel = 0;
rt->gcRunning = rt->gcRegenShapes = false;
@ -3758,134 +3845,3 @@ 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;
size_t rss = js_GetRSS();
/* Trigger a GC if the working set grew by more than 16MB and 12.5%. */
if (rss > (lastRSS + 16*1024*1024) && rss > (lastRSS + lastRSS/8))
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[128];
if (!statfd) {
snprintf(buf, 100, "/proc/%d/stat", getpid());
statfd = open(buf, O_RDONLY);
}
if (statfd < 0)
return 0;
lseek(statfd, 0, SEEK_SET);
int n = read(statfd, buf, sizeof(buf)-1);
if (n < 0)
return 0;
buf[n] = 0;
const char* p = strrchr(buf, ')');
if (!p)
return 0;
p = skipFields(p + 2, 20);
if (!p)
return 0;
return atol(p);
}
#endif

View File

@ -284,9 +284,6 @@ 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;
@ -329,15 +326,41 @@ 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(size_t *bytesp) : head(NULL), bytesp(bytesp) {}
JSFreePointerListTask() : head(NULL) {}
void add(void* ptr);
void run();
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;
}
}
};
#endif
@ -351,9 +374,6 @@ 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

View File

@ -4976,6 +4976,11 @@ 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);
@ -5064,6 +5069,7 @@ 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;

View File

@ -172,6 +172,7 @@ 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) {
@ -410,6 +411,9 @@ 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);

View File

@ -202,26 +202,6 @@ 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___ */

View File

@ -1144,12 +1144,16 @@ 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, "
@ -1176,6 +1180,11 @@ 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;

View File

@ -46,7 +46,6 @@
/* XPConnect JavaScript interactive shell. */
#include <stdio.h>
#include <signal.h>
#include "nsXULAppAPI.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
@ -156,86 +155,6 @@ static JSBool compileOnly = JS_FALSE;
JSPrincipals *gJSPrincipals = nsnull;
nsAutoString *gWorkingDirectory = nsnull;
static JSContext *gWatchdogContext = nsnull;
#ifdef XP_WIN
static HANDLE gTimerHandle = 0;
VOID CALLBACK
TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
{
JS_TriggerOperationCallback(gWatchdogContext);
}
static void
EnableWatchdog(JSContext *cx)
{
gWatchdogContext = cx;
if (gTimerHandle)
return;
if (!CreateTimerQueueTimer(&gTimerHandle,
NULL,
(WAITORTIMERCALLBACK)TimerCallback,
NULL,
DWORD(1000),
0,
WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE))
gTimerHandle = 0;
}
static void
DisableWatchdog()
{
if (gTimerHandle) {
DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
gTimerHandle = 0;
}
}
#else
static void
AlarmHandler(int sig)
{
JS_TriggerOperationCallback(gWatchdogContext);
}
static void
EnableWatchdog(JSContext *cx)
{
gWatchdogContext = cx;
signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */
alarm(1);
}
static void
DisableWatchdog()
{
alarm(0);
signal(SIGALRM, NULL);
}
#endif
class Watchdog {
public:
Watchdog(JSContext *cx) {
EnableWatchdog(cx);
}
~Watchdog() {
DisableWatchdog();
}
};
static JSBool
ShellOperationCallback(JSContext *cx)
{
JS_MaybeGC(cx);
return JS_TRUE;
}
static JSBool
GetLocationProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
@ -1604,7 +1523,6 @@ ContextCallback(JSContext *cx, uintN contextOp)
if (contextOp == JSCONTEXT_NEW) {
JS_SetErrorReporter(cx, my_ErrorReporter);
JS_SetVersion(cx, JSVERSION_LATEST);
JS_SetOperationCallback(cx, ShellOperationCallback);
}
return JS_TRUE;
}
@ -1732,8 +1650,6 @@ main(int argc, char **argv, char **envp)
return 1;
}
Watchdog watchdog(cx);
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (!xpc) {
printf("failed to get nsXPConnect service!\n");

View File

@ -6397,11 +6397,7 @@ free(void *ptr)
*/
size_t
#ifdef __linux__
malloc_usable_size(void *ptr)
#else
malloc_usable_size(const void *ptr)
#endif
{
#ifdef MALLOC_VALIDATE

View File

@ -92,11 +92,7 @@ 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. */