📄 jsgc.c
字号:
fprintf(stderr,"JS engine warning: 1 GC root remains after destroying the JSRuntime.\n"" This root may point to freed memory. Objects reachable\n"" through it have not been finalized.\n"); } else { fprintf(stderr,"JS engine warning: %lu GC roots remain after destroying the JSRuntime.\n"" These roots may point to freed memory. Objects reachable\n"" through them have not been finalized.\n", (unsigned long) leakedroots); } }#endif JS_DHashTableFinish(&rt->gcRootsHash); rt->gcRootsHash.ops = NULL; } if (rt->gcLocksHash) { JS_DHashTableDestroy(rt->gcLocksHash); rt->gcLocksHash = NULL; } rt->gcFreeList = NULL;}JSBooljs_AddRoot(JSContext *cx, void *rp, const char *name){ JSBool ok = js_AddRootRT(cx->runtime, rp, name); if (!ok) JS_ReportOutOfMemory(cx); return ok;}JSBooljs_AddRootRT(JSRuntime *rt, void *rp, const char *name){ JSBool ok; JSGCRootHashEntry *rhe; /* * Due to the long-standing, but now removed, use of rt->gcLock across the * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking * properly with a racing GC, without calling JS_AddRoot from a request. * We have to preserve API compatibility here, now that we avoid holding * rt->gcLock across the mark phase (including the root hashtable mark). * * If the GC is running and we're called on another thread, wait for this * GC activation to finish. We can safely wait here (in the case where we * are called within a request on another thread's context) without fear * of deadlock because the GC doesn't set rt->gcRunning until after it has * waited for all active requests to end. */ JS_LOCK_GC(rt);#ifdef JS_THREADSAFE JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); if (rt->gcRunning && rt->gcThread != js_CurrentThreadId()) { do { JS_AWAIT_GC_DONE(rt); } while (rt->gcLevel > 0); }#endif rhe = (JSGCRootHashEntry *) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_ADD); if (rhe) { rhe->root = rp; rhe->name = name; ok = JS_TRUE; } else { ok = JS_FALSE; } JS_UNLOCK_GC(rt); return ok;}JSBooljs_RemoveRoot(JSRuntime *rt, void *rp){ /* * Due to the JS_RemoveRootRT API, we may be called outside of a request. * Same synchronization drill as above in js_AddRoot. */ JS_LOCK_GC(rt);#ifdef JS_THREADSAFE JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); if (rt->gcRunning && rt->gcThread != js_CurrentThreadId()) { do { JS_AWAIT_GC_DONE(rt); } while (rt->gcLevel > 0); }#endif (void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE); rt->gcPoke = JS_TRUE; JS_UNLOCK_GC(rt); return JS_TRUE;}void *js_AllocGCThing(JSContext *cx, uintN flags){ JSBool tried_gc; JSRuntime *rt; JSGCThing *thing; uint8 *flagp;#ifdef TOO_MUCH_GC js_GC(cx, GC_KEEP_ATOMS); tried_gc = JS_TRUE;#else tried_gc = JS_FALSE;#endif rt = cx->runtime; JS_LOCK_GC(rt); JS_ASSERT(!rt->gcRunning); if (rt->gcRunning) { METER(rt->gcStats.finalfail++); JS_UNLOCK_GC(rt); return NULL; } METER(rt->gcStats.alloc++);retry: thing = rt->gcFreeList; if (thing) { rt->gcFreeList = thing->next; flagp = thing->flagp; METER(rt->gcStats.freelen--); METER(rt->gcStats.recycle++); } else { if (rt->gcBytes < rt->gcMaxBytes && (tried_gc || rt->gcMallocBytes < rt->gcMaxBytes)) { /* * Inline form of JS_ARENA_ALLOCATE adapted to truncate the current * arena's limit to a GC_PAGE_SIZE boundary, and to skip over every * GC_PAGE_SIZE-byte-aligned thing (which is actually not a thing, * it's a JSGCPageInfo record). */ JSArenaPool *pool = &rt->gcArenaPool; JSArena *a = pool->current; size_t nb = sizeof(JSGCThing); jsuword p = a->avail; jsuword q = p + nb; if (q > (a->limit & ~GC_PAGE_MASK)) { thing = gc_new_arena(pool); } else { if ((p & GC_PAGE_MASK) == 0) { /* Beware, p points to a JSGCPageInfo record! */ p = q; q += nb; JS_ArenaCountAllocation(pool, nb); } a->avail = q; thing = (JSGCThing *)p; } JS_ArenaCountAllocation(pool, nb); } /* * Consider doing a "last ditch" GC if thing couldn't be allocated. * * Keep rt->gcLock across the call into js_GC so we don't starve and * lose to racing threads who deplete the heap just after js_GC has * replenished it (or has synchronized with a racing GC that collected * a bunch of garbage). This unfair scheduling can happen on certain * operating systems. For the gory details, see Mozilla bug 162779 * (http://bugzilla.mozilla.org/show_bug.cgi?id=162779). */ if (!thing) { if (!tried_gc) { rt->gcPoke = JS_TRUE; js_GC(cx, GC_KEEP_ATOMS | GC_ALREADY_LOCKED); tried_gc = JS_TRUE; METER(rt->gcStats.retry++); goto retry; } METER(rt->gcStats.fail++); JS_UNLOCK_GC(rt); JS_ReportOutOfMemory(cx); return NULL; } /* Find the flags pointer given thing's address. */ flagp = js_GetGCThingFlags(thing); } *flagp = (uint8)flags; rt->gcBytes += sizeof(JSGCThing) + sizeof(uint8); cx->newborn[flags & GCF_TYPEMASK] = thing; /* * Clear thing before unlocking in case a GC run is about to scan it, * finding it via cx->newborn[]. */ thing->next = NULL; thing->flagp = NULL; JS_UNLOCK_GC(rt); return thing;}JSBooljs_LockGCThing(JSContext *cx, void *thing){ JSBool ok = js_LockGCThingRT(cx->runtime, thing); if (!ok) JS_ReportOutOfMemory(cx); return ok;}JSBooljs_LockGCThingRT(JSRuntime *rt, void *thing){ uint8 *flagp, flags, lockbits; JSBool ok; JSGCLockHashEntry *lhe; if (!thing) return JS_TRUE; flagp = js_GetGCThingFlags(thing); flags = *flagp; ok = JS_FALSE; JS_LOCK_GC(rt); lockbits = (flags & GCF_LOCKMASK); if (lockbits != GCF_LOCKMASK) { if ((flags & GCF_TYPEMASK) == GCX_OBJECT) { /* Objects may require "deep locking", i.e., rooting by value. */ if (lockbits == 0) { if (!rt->gcLocksHash) { rt->gcLocksHash = JS_NewDHashTable(JS_DHashGetStubOps(), NULL, sizeof(JSGCLockHashEntry), GC_ROOTS_SIZE); if (!rt->gcLocksHash) goto error; } else {#ifdef DEBUG JSDHashEntryHdr *hdr = JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_LOOKUP); JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(hdr));#endif } lhe = (JSGCLockHashEntry *) JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_ADD); if (!lhe) goto error; lhe->thing = thing; lhe->count = 1; *flagp = (uint8)(flags + GCF_LOCK); } else { JS_ASSERT(lockbits == GCF_LOCK); lhe = (JSGCLockHashEntry *) JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_LOOKUP); JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&lhe->hdr)); if (JS_DHASH_ENTRY_IS_BUSY(&lhe->hdr)) { JS_ASSERT(lhe->count >= 1); lhe->count++; } } } else { *flagp = (uint8)(flags + GCF_LOCK); } } else { METER(rt->gcStats.stuck++); } METER(rt->gcStats.lock++); ok = JS_TRUE;error: JS_UNLOCK_GC(rt); return ok;}JSBooljs_UnlockGCThingRT(JSRuntime *rt, void *thing){ uint8 *flagp, flags, lockbits; JSGCLockHashEntry *lhe; if (!thing) return JS_TRUE; flagp = js_GetGCThingFlags(thing); flags = *flagp; JS_LOCK_GC(rt); lockbits = (flags & GCF_LOCKMASK); if (lockbits != GCF_LOCKMASK) { if ((flags & GCF_TYPEMASK) == GCX_OBJECT) { /* Defend against a call on an unlocked object. */ if (lockbits != 0) { JS_ASSERT(lockbits == GCF_LOCK); lhe = (JSGCLockHashEntry *) JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_LOOKUP); JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&lhe->hdr)); if (JS_DHASH_ENTRY_IS_BUSY(&lhe->hdr) && --lhe->count == 0) { (void) JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_REMOVE); *flagp = (uint8)(flags & ~GCF_LOCKMASK); } } } else { *flagp = (uint8)(flags - GCF_LOCK); } } else { METER(rt->gcStats.unstuck++); } rt->gcPoke = JS_TRUE; METER(rt->gcStats.unlock++); JS_UNLOCK_GC(rt); return JS_TRUE;}#ifdef GC_MARK_DEBUG#include <stdio.h>#include <stdlib.h>#include "jsprf.h"JS_FRIEND_DATA(FILE *) js_DumpGCHeap;JS_EXPORT_DATA(void *) js_LiveThingToFind;#ifdef HAVE_XPCONNECT#include "dump_xpc.h"#endifstatic const char *gc_object_class_name(void* thing){ uint8 *flagp = js_GetGCThingFlags(thing); const char *className = ""; static char depbuf[32]; switch (*flagp & GCF_TYPEMASK) { case GCX_OBJECT: { JSObject *obj = (JSObject *)thing; JSClass *clasp = JSVAL_TO_PRIVATE(obj->slots[JSSLOT_CLASS]); className = clasp->name;#ifdef HAVE_XPCONNECT if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { jsval privateValue = obj->slots[JSSLOT_PRIVATE]; JS_ASSERT(clasp->flags & JSCLASS_HAS_PRIVATE); if (!JSVAL_IS_VOID(privateValue)) { void *privateThing = JSVAL_TO_PRIVATE(privateValue); const char *xpcClassName = GetXPCObjectClassName(privateThing); if (xpcClassName) className = xpcClassName; } }#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -