📄 jsgc.c
字号:
a->prev = arenaList->last; a->prevUnscanned = NULL; a->unscannedPages = 0; arenaList->last = a; arenaList->lastLimit = 0; bytesptr = (arenaList == &rt->gcArenaList[0]) ? &rt->gcBytes : &rt->gcPrivateBytes; *bytesptr += GC_ARENA_SIZE; return JS_TRUE;}static voidDestroyGCArena(JSRuntime *rt, JSGCArenaList *arenaList, JSGCArena **ap){ JSGCArena *a; uint32 *bytesptr; a = *ap; JS_ASSERT(a); bytesptr = (arenaList == &rt->gcArenaList[0]) ? &rt->gcBytes : &rt->gcPrivateBytes; JS_ASSERT(*bytesptr >= GC_ARENA_SIZE); *bytesptr -= GC_ARENA_SIZE; METER(rt->gcStats.afree++); METER(--arenaList->stats.narenas); if (a == arenaList->last) arenaList->lastLimit = (uint16)(a->prev ? GC_THINGS_SIZE : 0); *ap = a->prev;#ifdef DEBUG memset(a, JS_FREE_PATTERN, GC_ARENA_SIZE);#endif free(a);}static voidInitGCArenaLists(JSRuntime *rt){ uintN i, thingSize; JSGCArenaList *arenaList; for (i = 0; i < GC_NUM_FREELISTS; i++) { arenaList = &rt->gcArenaList[i]; thingSize = GC_FREELIST_NBYTES(i); JS_ASSERT((size_t)(uint16)thingSize == thingSize); arenaList->last = NULL; arenaList->lastLimit = 0; arenaList->thingSize = (uint16)thingSize; arenaList->freeList = NULL; METER(memset(&arenaList->stats, 0, sizeof arenaList->stats)); }}static voidFinishGCArenaLists(JSRuntime *rt){ uintN i; JSGCArenaList *arenaList; for (i = 0; i < GC_NUM_FREELISTS; i++) { arenaList = &rt->gcArenaList[i]; while (arenaList->last) DestroyGCArena(rt, arenaList, &arenaList->last); arenaList->freeList = NULL; }}uint8 *js_GetGCThingFlags(void *thing){ JSGCPageInfo *pi; jsuword offsetInArena, thingIndex; pi = THING_TO_PAGE(thing); offsetInArena = pi->offsetInArena; JS_ASSERT(offsetInArena < GC_THINGS_SIZE); thingIndex = ((offsetInArena & ~GC_PAGE_MASK) | ((jsuword)thing & GC_PAGE_MASK)) / sizeof(JSGCThing); JS_ASSERT(thingIndex < GC_PAGE_SIZE); if (thingIndex >= (offsetInArena & GC_PAGE_MASK)) thingIndex += GC_THINGS_SIZE; return (uint8 *)pi - offsetInArena + thingIndex;}JSRuntime*js_GetGCStringRuntime(JSString *str){ JSGCPageInfo *pi; JSGCArenaList *list; pi = THING_TO_PAGE(str); list = PAGE_TO_ARENA(pi)->list; JS_ASSERT(list->thingSize == sizeof(JSGCThing)); JS_ASSERT(GC_FREELIST_INDEX(sizeof(JSGCThing)) == 0); return (JSRuntime *)((uint8 *)list - offsetof(JSRuntime, gcArenaList));}JSBooljs_IsAboutToBeFinalized(JSContext *cx, void *thing){ uint8 flags = *js_GetGCThingFlags(thing); return !(flags & (GCF_MARK | GCF_LOCK | GCF_FINAL));}typedef void (*GCFinalizeOp)(JSContext *cx, JSGCThing *thing);#ifndef DEBUG# define js_FinalizeDouble NULL#endif#if !JS_HAS_XML_SUPPORT# define js_FinalizeXMLNamespace NULL# define js_FinalizeXMLQName NULL# define js_FinalizeXML NULL#endifstatic GCFinalizeOp gc_finalizers[GCX_NTYPES] = { (GCFinalizeOp) js_FinalizeObject, /* GCX_OBJECT */ (GCFinalizeOp) js_FinalizeString, /* GCX_STRING */ (GCFinalizeOp) js_FinalizeDouble, /* GCX_DOUBLE */ (GCFinalizeOp) js_FinalizeString, /* GCX_MUTABLE_STRING */ NULL, /* GCX_PRIVATE */ (GCFinalizeOp) js_FinalizeXMLNamespace, /* GCX_NAMESPACE */ (GCFinalizeOp) js_FinalizeXMLQName, /* GCX_QNAME */ (GCFinalizeOp) js_FinalizeXML, /* GCX_XML */ NULL, /* GCX_EXTERNAL_STRING */ NULL, NULL, NULL, NULL, NULL, NULL, NULL};#ifdef GC_MARK_DEBUGstatic const char newborn_external_string[] = "newborn external string";static const char *gc_typenames[GCX_NTYPES] = { "newborn object", "newborn string", "newborn double", "newborn mutable string", "newborn private", "newborn Namespace", "newborn QName", "newborn XML", newborn_external_string, newborn_external_string, newborn_external_string, newborn_external_string, newborn_external_string, newborn_external_string, newborn_external_string, newborn_external_string};#endifintNjs_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, JSStringFinalizeOp newop){ uintN i; for (i = GCX_EXTERNAL_STRING; i < GCX_NTYPES; i++) { if (gc_finalizers[i] == (GCFinalizeOp) oldop) { gc_finalizers[i] = (GCFinalizeOp) newop; return (intN) i; } } return -1;}/* This is compatible with JSDHashEntryStub. */typedef struct JSGCRootHashEntry { JSDHashEntryHdr hdr; void *root; const char *name;} JSGCRootHashEntry;/* Initial size of the gcRootsHash table (SWAG, small enough to amortize). */#define GC_ROOTS_SIZE 256#define GC_FINALIZE_LEN 1024JSBooljs_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 */ /* * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes * for default backward API compatibility. */ rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes; return JS_TRUE;}#ifdef JS_GCMETERJS_FRIEND_API(void)js_DumpGCStats(JSRuntime *rt, FILE *fp){ uintN i; size_t totalThings, totalMaxThings, totalBytes; fprintf(fp, "\nGC allocation statistics:\n");#define UL(x) ((unsigned long)(x))#define ULSTAT(x) UL(rt->gcStats.x) totalThings = 0; totalMaxThings = 0; totalBytes = 0; for (i = 0; i < GC_NUM_FREELISTS; i++) { JSGCArenaList *list = &rt->gcArenaList[i]; JSGCArenaStats *stats = &list->stats; if (stats->maxarenas == 0) { fprintf(fp, "ARENA LIST %u (thing size %lu): NEVER USED\n", i, UL(GC_FREELIST_NBYTES(i))); continue; } fprintf(fp, "ARENA LIST %u (thing size %lu):\n", i, UL(GC_FREELIST_NBYTES(i))); fprintf(fp, " arenas: %lu\n", UL(stats->narenas)); fprintf(fp, " max arenas: %lu\n", UL(stats->maxarenas)); fprintf(fp, " things: %lu\n", UL(stats->nthings)); fprintf(fp, " max things: %lu\n", UL(stats->maxthings)); fprintf(fp, " free list: %lu\n", UL(stats->freelen)); fprintf(fp, " free list density: %.1f%%\n", stats->narenas == 0 ? 0.0 : (100.0 * list->thingSize * (jsdouble)stats->freelen / (GC_THINGS_SIZE * (jsdouble)stats->narenas))); fprintf(fp, " average free list density: %.1f%%\n", stats->totalarenas == 0 ? 0.0 : (100.0 * list->thingSize * (jsdouble)stats->totalfreelen / (GC_THINGS_SIZE * (jsdouble)stats->totalarenas))); fprintf(fp, " recycles: %lu\n", UL(stats->recycle)); fprintf(fp, " recycle/alloc ratio: %.2f\n", (jsdouble)stats->recycle / (jsdouble)(stats->totalnew - stats->recycle)); totalThings += stats->nthings; totalMaxThings += stats->maxthings; totalBytes += GC_FREELIST_NBYTES(i) * stats->nthings; } fprintf(fp, "TOTAL STATS:\n"); fprintf(fp, " public bytes allocated: %lu\n", UL(rt->gcBytes)); fprintf(fp, " private bytes allocated: %lu\n", UL(rt->gcPrivateBytes)); fprintf(fp, " alloc attempts: %lu\n", ULSTAT(alloc));#ifdef JS_THREADSAFE fprintf(fp, " alloc without locks: %1u\n", ULSTAT(localalloc));#endif fprintf(fp, " total GC things: %lu\n", UL(totalThings)); fprintf(fp, " max total GC things: %lu\n", UL(totalMaxThings)); fprintf(fp, " GC things size: %lu\n", UL(totalBytes)); fprintf(fp, "allocation retries after GC: %lu\n", ULSTAT(retry)); fprintf(fp, " allocation failures: %lu\n", ULSTAT(fail)); fprintf(fp, " things born locked: %lu\n", ULSTAT(lockborn)); fprintf(fp, " valid lock calls: %lu\n", ULSTAT(lock)); fprintf(fp, " valid unlock calls: %lu\n", ULSTAT(unlock)); fprintf(fp, " mark recursion depth: %lu\n", ULSTAT(depth)); fprintf(fp, " maximum mark recursion: %lu\n", ULSTAT(maxdepth)); fprintf(fp, " mark C recursion depth: %lu\n", ULSTAT(cdepth)); fprintf(fp, " maximum mark C recursion: %lu\n", ULSTAT(maxcdepth)); fprintf(fp, " delayed scan bag adds: %lu\n", ULSTAT(unscanned));#ifdef DEBUG fprintf(fp, " max delayed scan bag size: %lu\n", ULSTAT(maxunscanned));#endif fprintf(fp, " maximum GC nesting level: %lu\n", ULSTAT(maxlevel)); fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke)); fprintf(fp, " useless GC calls: %lu\n", ULSTAT(nopoke)); fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree)); fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg)); fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots)); fprintf(fp, "reachable closeable objects: %lu\n", ULSTAT(nclose)); fprintf(fp, " max reachable closeable: %lu\n", ULSTAT(maxnclose)); fprintf(fp, " scheduled close hooks: %lu\n", ULSTAT(closelater)); fprintf(fp, " max scheduled close hooks: %lu\n", ULSTAT(maxcloselater));#undef UL#undef US#ifdef JS_ARENAMETER JS_DumpArenaStats(fp);#endif}#endif#ifdef DEBUGstatic voidCheckLeakedRoots(JSRuntime *rt);#endifvoidjs_FinishGC(JSRuntime *rt){#ifdef JS_ARENAMETER JS_DumpArenaStats(stdout);#endif#ifdef JS_GCMETER js_DumpGCStats(rt, stdout);#endif FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo);#if JS_HAS_GENERATORS rt->gcCloseState.reachableList = NULL; METER(rt->gcStats.nclose = 0); rt->gcCloseState.todoQueue = NULL;#endif FinishGCArenaLists(rt); if (rt->gcRootsHash.ops) {#ifdef DEBUG CheckLeakedRoots(rt);#endif JS_DHashTableFinish(&rt->gcRootsHash); rt->gcRootsHash.ops = NULL; } if (rt->gcLocksHash) { JS_DHashTableDestroy(rt->gcLocksHash); rt->gcLocksHash = 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->id != 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->id != 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;}#ifdef DEBUGJS_STATIC_DLL_CALLBACK(JSDHashOperator)js_root_printer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 i, void *arg){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -