--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -853,32 +853,28 @@ PrintWinCodebase(nsGlobalWindow *win)
}
nsCAutoString spec;
uri->GetSpec(spec);
printf("%s\n", spec.get());
}
#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;
- }
- if (PR_IntervalToMilliseconds(PR_IntervalNow() - startTime) < 10000)
- return;
- startTime = 0;
+ 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);
}
- JS_MaybeGC(cx);
}
static already_AddRefed<nsIPrompt>
GetPromptFromContext(nsJSContext* ctx)
{
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(ctx->GetGlobalObject()));
NS_ENSURE_TRUE(win, nsnull);
@@ -3878,20 +3874,40 @@ ReportAllJSExceptionsPrefChangedCallback
return 0;
}
static int
SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
{
PRInt32 highwatermark = nsContentUtils::GetIntPref(aPrefName, 32);
- JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES,
- (highwatermark >= 32)
- ? 0xffffffff
- : highwatermark * 1024L * 1024L);
+ 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,
+ 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;
}
static JSPrincipals *
ObjectPrincipalFinder(JSContext *cx, JSObject *obj)
{
if (!sSecurityManager)
return nsnull;
@@ -3974,16 +3990,22 @@ nsJSRuntime::Init()
nsnull);
nsContentUtils::RegisterPrefCallback("javascript.options.mem.high_water_mark",
SetMemoryHighWaterMarkPrefChangedCallback,
nsnull);
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);
nsIObserver* activityObserver = new nsUserActivityObserver();
NS_ENSURE_TRUE(activityObserver, NS_ERROR_OUT_OF_MEMORY);
obs->AddObserver(activityObserver, "user-interaction-inactive", PR_FALSE);
obs->AddObserver(activityObserver, "user-interaction-active", PR_FALSE);
obs->AddObserver(activityObserver, "xpcom-shutdown", PR_FALSE);
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1947,17 +1947,17 @@ case "$target" in
MKSHLIB_FORCE_ALL=
MKSHLIB_UNFORCE_ALL=
DSO_LDOPTS=-SUBSYSTEM:WINDOWS
_USE_CPP_INCLUDE_FLAG=1
_DEFINES_CFLAGS='-FI $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT'
_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'
MOZ_OPTIMIZE_FLAGS='-O1'
MOZ_JS_LIBS='$(libdir)/js$(MOZ_BITS)$(VERSION_NUMBER).lib'
MOZ_FIX_LINK_PATHS=
DYNAMIC_XPCOM_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcom_core.lib'
XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib'
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2447,17 +2447,82 @@ JS_GC(JSContext *cx)
if (cx->tempPool.current == &cx->tempPool.first)
JS_FinishArenaPool(&cx->tempPool);
js_GC(cx, GC_NORMAL);
}
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)
JS_SetGCCallback(JSContext *cx, JSGCCallback cb)
{
CHECK_REQUEST(cx);
return JS_SetGCCallbackRT(cx->runtime, cb);
}
@@ -2481,31 +2546,42 @@ JS_IsAboutToBeFinalized(JSContext *cx, v
JS_PUBLIC_API(void)
JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value)
{
switch (key) {
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;
}
}
JS_PUBLIC_API(uint32)
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:
JS_ASSERT(key == JSGC_NUMBER);
return rt->gcNumber;
}
}
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1266,27 +1266,40 @@ JS_IsGCMarkingTracer(JSTracer *trc);
extern JS_PUBLIC_API(JSBool)
JS_IsAboutToBeFinalized(JSContext *cx, void *thing);
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)
JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value);
extern JS_PUBLIC_API(uint32)
JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1792,17 +1792,16 @@ js_InvokeOperationCallback(JSContext *cx
void
js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked)
{
JSContext *acx, *iter;
#ifdef JS_THREADSAFE
if (!gcLocked)
JS_LOCK_GC(rt);
#endif
-
iter = NULL;
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)))
JS_TriggerOperationCallback(acx);
#ifdef JS_THREADSAFE
if (!gcLocked)
JS_UNLOCK_GC(rt);
#endif
}
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -257,16 +257,22 @@ struct JSThreadData {
/* Lock-free hashed lists of scripts created by eval to garbage-collect. */
JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE];
#ifdef JS_EVAL_CACHE_METERING
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.
*/
JSFreePointerListTask *deallocatorTask;
#endif
void mark(JSTracer *trc) {
@@ -379,24 +385,26 @@ struct JSRuntime {
/* Garbage collector state, used by jsgc.c. */
JSGCChunkInfo *gcChunkList;
JSGCArenaList gcArenaList[GC_NUM_FREELISTS];
JSGCDoubleArenaList gcDoubleArenaList;
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;
/*
* NB: do not pack another flag here by claiming gcPadding unless the new
* flag is written only by the GC thread. Atomic updates to packed bytes
* are not guaranteed, so stores issued by one thread may be lost due to
* unsynchronized read-modify-write cycles on other threads.
@@ -416,16 +424,17 @@ struct JSRuntime {
*/
uint8 gcRegenShapesScopeFlag;
#ifdef JS_GC_ZEAL
jsrefcount gcZeal;
#endif
JSGCCallback gcCallback;
+ size_t gcMallocBytes;
JSGCArenaInfo *gcUntracedArenaStackTop;
#ifdef DEBUG
size_t gcTraceLaterCount;
#endif
/*
* Table for tracking iterators to ensure that we close iterator's state
* before finalizing the iterable object.
@@ -704,16 +713,19 @@ struct JSRuntime {
JSGCStats gcStats;
#endif
#ifdef JS_FUNCTION_METERING
JSFunctionMeter functionMeter;
char lastScriptFilename[1024];
#endif
+ void setGCTriggerFactor(uint32 factor);
+ void setGCLastBytes(size_t lastBytes);
+
inline void* malloc(size_t bytes) {
return ::js_malloc(bytes);
}
inline void* calloc(size_t bytes) {
return ::js_calloc(bytes);
}
@@ -1063,58 +1075,72 @@ struct JSContext {
VMSideExit *bailExit;
/* Used when calling natives from trace to root the vp vector. */
uintN nativeVpLen;
jsval *nativeVp;
#endif
#ifdef JS_THREADSAFE
- inline void createDeallocatorTask(size_t *bytesp) {
- JSThreadData *tls = JS_THREAD_DATA(this);
+ 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() {
- JSThreadData *tls = JS_THREAD_DATA(this);
+ JSThreadData* tls = JS_THREAD_DATA(this);
if (tls->deallocatorTask) {
runtime->deallocatorThread->schedule(tls->deallocatorTask);
tls->deallocatorTask = NULL;
}
}
#endif
- inline void *malloc(size_t bytes) {
+ /* 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);
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) {
+ 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;
}
#ifdef JS_THREADSAFE
inline void free(void* p) {
if (!p)
return;
if (thread) {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1293,23 +1293,34 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes
InitGCArenaLists(rt);
if (!JS_DHashTableInit(&rt->gcRootsHash, JS_DHashGetStubOps(), NULL,
sizeof(JSGCRootHashEntry), GC_ROOTS_SIZE)) {
rt->gcRootsHash.ops = NULL;
return JS_FALSE;
}
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;
}
#ifdef JS_GCMETER
static void
@@ -1708,32 +1719,69 @@ CloseNativeIterators(JSContext *cx)
static struct GCHist {
bool lastDitch;
JSGCThing *freeList;
} gchist[NGCHIST];
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)
{
JSRuntime *rt;
bool doGC;
JSGCThing *thing;
uint8 *flagp;
JSGCArenaList *arenaList;
JSGCArenaInfo *a;
uintN thingsLimit;
JSLocalRootStack *lrs;
#ifdef JS_GCMETER
JSGCArenaStats *astats;
#endif
#ifdef JS_THREADSAFE
JSBool gcLocked;
+ uintN localMallocBytes;
JSGCThing **lastptr;
JSGCThing *tmpthing;
uint8 *tmpflagp;
uintN maxFreeThings; /* max to take from the global free list */
#endif
JS_ASSERT((flags & GCF_TYPEMASK) != GCX_DOUBLE);
rt = cx->runtime;
@@ -1746,41 +1794,50 @@ NewGCThing(JSContext *cx, uintN flags)
METER(astats->alloc++);
#ifdef JS_THREADSAFE
gcLocked = JS_FALSE;
JS_ASSERT(cx->thread);
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++);
goto success;
}
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) {
METER(rt->gcStats.finalfail++);
JS_UNLOCK_GC(rt);
return NULL;
}
#if defined JS_GC_ZEAL && defined JS_TRACER
if (rt->gcZeal >= 1 && JS_TRACE_MONITOR(cx).useReservedObjects)
goto testReservedObjects;
#endif
arenaList = &rt->gcArenaList[flindex];
- doGC = rt->gcIsNeeded;
+ doGC = IsGCThresholdReached(rt);
for (;;) {
if (doGC
#ifdef JS_TRACER
&& !JS_ON_TRACE(cx) && !JS_TRACE_MONITOR(cx).useReservedObjects
#endif
) {
/*
* Keep rt->gcLock across the call into js_GC so we don't starve
@@ -1799,20 +1856,23 @@ NewGCThing(JSContext *cx, uintN flags)
if (thing) {
arenaList->freeList = thing->next;
flagp = thing->flagp;
JS_ASSERT(*flagp & GCF_FINAL);
#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;
if (tmpthing) {
maxFreeThings = MAX_THREAD_LOCAL_THINGS;
do {
if (!tmpthing->next)
break;
@@ -1872,17 +1932,17 @@ testReservedObjects:
arenaList->lastCount++;
#ifdef JS_THREADSAFE
/*
* Refill the local free list by taking free things from the last
* 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;
if (maxFreeThings > MAX_THREAD_LOCAL_THINGS)
maxFreeThings = MAX_THREAD_LOCAL_THINGS;
uint32 lastCount = arenaList->lastCount;
while (maxFreeThings != 0) {
--maxFreeThings;
@@ -1999,17 +2059,17 @@ RefillDoubleFreeList(JSContext *cx)
JS_ASSERT(!rt->gcRunning);
if (rt->gcRunning) {
METER(rt->gcStats.finalfail++);
JS_UNLOCK_GC(rt);
return NULL;
}
- if (rt->gcIsNeeded)
+ if (IsGCThresholdReached(rt))
goto do_gc;
/*
* Loop until we find a flag bitmap byte with unset bits indicating free
* double cells, then set all bits as used and put the cells to the free
* list for the current context.
*/
doubleFlags = rt->gcDoubleArenaList.nextDoubleFlags;
@@ -2186,16 +2246,55 @@ js_ReserveObjects(JSContext *cx, size_t
obj->fslots[1] = INT_TO_JSVAL(i);
head = obj;
}
return JS_TRUE;
}
#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
* is only used for non-dependant strings.
*/
#define GC_THING_IS_SHALLOW(flagp, thing) \
((flagp) && \
((*(flagp) & GCF_TYPEMASK) >= GCX_EXTERNAL_STRING || \
@@ -3392,16 +3491,19 @@ js_GC(JSContext *cx, JSGCInvocationKind
if (JS_ON_TRACE(cx))
goto out;
#endif
VOUCH_HAVE_STACK();
/* 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);
}
#endif
#ifdef JS_TRACER
js_PurgeJITOracle();
@@ -3452,24 +3554,17 @@ js_GC(JSContext *cx, JSGCInvocationKind
JS_ASSERT(cx->insideGCMarkCallback);
cx->insideGCMarkCallback = JS_FALSE;
}
JS_ASSERT(rt->gcTraceLaterCount == 0);
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
/*
* Sweep phase.
*
* Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set
* so that any attempt to allocate a GC-thing from a finalizer will fail,
* rather than nest badly and leave the unmarked newborn to be swept.
@@ -3640,26 +3735,17 @@ js_GC(JSContext *cx, JSGCInvocationKind
js_SweepScriptFilenames(rt);
/*
* Destroy arenas after we finished the sweeping sofinalizers can safely
* use js_IsAboutToBeFinalized().
*/
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
if (rt->gcCallback)
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
#ifdef DEBUG_srcnotesize
{ extern void DumpSrcNoteSizeHist();
DumpSrcNoteSizeHist();
@@ -3707,16 +3793,17 @@ out:
if (!JS_ON_TRACE(cx) && (rt->gcLevel > 1 || rt->gcPoke)) {
VOUCH_HAVE_STACK();
rt->gcLevel = 1;
rt->gcPoke = JS_FALSE;
JS_UNLOCK_GC(rt);
goto restart;
}
+ rt->setGCLastBytes(rt->gcBytes);
done_running:
rt->gcLevel = 0;
rt->gcRunning = rt->gcRegenShapes = false;
#ifdef JS_THREADSAFE
rt->gcThread = NULL;
JS_NOTIFY_GC_DONE(rt);
@@ -3758,139 +3845,8 @@ out:
/*
* On shutdown iterate until JSGC_END callback stops creating
* garbage.
*/
goto restart_at_beginning;
}
}
}
-
-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 32MB and at least 25%. */
- if (rss > lastRSS + 32*1024*1024 && rss > lastRSS + lastRSS/4)
- 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
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -279,19 +279,16 @@ typedef enum JSGCInvocationKind {
* jsgc.c just before js_GC's definition for details.
*/
GC_LAST_DITCH = GC_LOCK_HELD | 2
} 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;
struct JSGCArenaList {
JSGCArenaInfo *last; /* last allocated GC arena */
uint32 lastCount; /* number of allocated things in the last
arena */
@@ -324,41 +321,64 @@ struct JSWeakRoots {
jsval lastAtom;
/* Root for the result of the most recent js_InternalInvoke call. */
jsval lastInternalResult;
};
#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**)ptr = head;
+ head = ptr;
+ }
- void add(void* ptr);
- void run();
+ void run() {
+ void *ptr = head;
+ while (ptr) {
+ void *next = *(void **)ptr;
+ js_free(ptr);
+ ptr = next;
+ }
+ }
};
#endif
/*
* Free the chars held by str when it is finalized by the GC. When type is
* less then zero, it denotes an internal string. Otherwise it denotes the
* type of the external string allocated with JS_NewExternalString.
*
* This function always needs rt but can live with null cx.
*/
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
#ifdef JS_GCMETER
typedef struct JSGCArenaStats {
uint32 alloc; /* allocation attempts */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4966,16 +4966,21 @@ js_Enumerate(JSContext *cx, JSObject *ob
JS_ASSERT(length == 0);
JS_ASSERT(allocated == 0);
*statep = JSVAL_ZERO;
} else {
JS_ASSERT(length != 0);
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);
*cachep = (jsuword) ne;
JS_UNLOCK_GC(cx->runtime);
}
*statep = PRIVATE_TO_JSVAL(ne);
}
@@ -5054,16 +5059,17 @@ js_TraceNativeEnumerators(JSTracer *trc)
if (ne->cursor != 0) {
/* Trace ids of the running enumerator. */
cursor = ne->ids;
end = cursor + ne->length;
do {
js_TraceId(trc, *cursor);
} while (++cursor != end);
} else if (doGC) {
+ js_RemoveAsGCBytes(rt, NativeEnumeratorSize(ne->length));
*nep = ne->next;
trc->context->free(ne);
continue;
}
nep = &ne->next;
}
}
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -167,16 +167,17 @@ JSScope::createTable(JSContext *cx, bool
}
table = (JSScopeProperty **) js_calloc(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
if (!table) {
if (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) {
spp = search(sprop->id, true);
SPROP_STORE_PRESERVING_COLLISION(spp, sprop);
}
return true;
}
@@ -405,16 +406,19 @@ JSScope::changeTable(JSContext *cx, int
return false;
/* Now that we have newtable allocated, update members. */
hashShift = JS_DHASH_BITS - newlog2;
removedCount = 0;
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);
if (sprop) {
spp = search(sprop->id, true);
JS_ASSERT(SPROP_IS_FREE(*spp));
*spp = sprop;
}
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -197,31 +197,11 @@ static JS_INLINE void* js_realloc(void*
bytes = sizeof(void*);
return realloc(p, bytes);
}
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___ */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1139,22 +1139,26 @@ GCParameter(JSContext *cx, uintN argc, j
return JS_FALSE;
vp[2] = STRING_TO_JSVAL(str);
}
paramName = JS_GetStringBytes(str);
if (!paramName)
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, "
"maxMallocBytes, gcStackpoolLifespan, gcBytes, "
"gcNumber or gcTriggerFactor");
return JS_FALSE;
}
@@ -1171,16 +1175,21 @@ GCParameter(JSContext *cx, uintN argc, j
}
if (!JS_ValueToECMAUint32(cx, vp[3], &value)) {
JS_ReportError(cx,
"the second argument must be convertable to uint32 "
"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;
}
#ifdef JS_GC_ZEAL
static JSBool
GCZeal(JSContext *cx, uintN argc, jsval *vp)
--- a/js/src/xpconnect/shell/xpcshell.cpp
+++ b/js/src/xpconnect/shell/xpcshell.cpp
@@ -145,96 +145,16 @@ FILE *gInFile = NULL;
int gExitCode = 0;
JSBool gQuitting = JS_FALSE;
static JSBool reportWarnings = JS_TRUE;
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,
- cx->runtime,
- 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)
{
#if (!defined(XP_WIN) && !defined(XP_UNIX)) || defined(WINCE)
//XXX: your platform should really implement this
return JS_FALSE;
#else
JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL);
@@ -1593,17 +1513,16 @@ static JSBool
ContextCallback(JSContext *cx, uintN contextOp)
{
if (gOldJSContextCallback && !gOldJSContextCallback(cx, contextOp))
return JS_FALSE;
if (contextOp == JSCONTEXT_NEW) {
JS_SetErrorReporter(cx, my_ErrorReporter);
JS_SetVersion(cx, JSVERSION_LATEST);
- JS_SetOperationCallback(cx, ShellOperationCallback);
}
return JS_TRUE;
}
static bool
GetCurrentWorkingDirectory(nsAString& workingDirectory)
{
#if (!defined(XP_WIN) && !defined(XP_UNIX)) || defined(WINCE)
@@ -1721,18 +1640,16 @@ main(int argc, char **argv, char **envp)
gOldJSContextCallback = JS_SetContextCallback(rt, ContextCallback);
cx = JS_NewContext(rt, 8192);
if (!cx) {
printf("JS_NewContext failed!\n");
return 1;
}
- Watchdog watchdog(cx);
-
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (!xpc) {
printf("failed to get nsXPConnect service!\n");
return 1;
}
// Since the caps security system might set a default security manager
// we will be sure that the secman on this context gives full trust.
--- a/memory/jemalloc/jemalloc.c
+++ b/memory/jemalloc/jemalloc.c
@@ -6392,21 +6392,17 @@ free(void *ptr)
* End malloc(3)-compatible functions.
*/
/******************************************************************************/
/*
* Begin non-standard functions.
*/
size_t
-#ifdef __linux__
-malloc_usable_size(void *ptr)
-#else
malloc_usable_size(const void *ptr)
-#endif
{
#ifdef MALLOC_VALIDATE
return (isalloc_validate(ptr));
#else
assert(ptr != NULL);
return (isalloc(ptr));
--- a/memory/jemalloc/jemalloc.h
+++ b/memory/jemalloc/jemalloc.h
@@ -87,21 +87,17 @@ void *malloc(size_t size);
void *valloc(size_t size);
void *calloc(size_t num, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
#endif
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. */
void *xmalloc(size_t size);
void *xcalloc(size_t num, size_t size);
void *xrealloc(void *ptr, size_t size);
void *xmemalign(size_t alignment, size_t size);