📄 jsgc.c
字号:
/* Bump gcLevel and return rather than nest on this thread. */ currentThread = js_CurrentThreadId(); if (rt->gcThread == currentThread) { JS_ASSERT(rt->gcLevel > 0); rt->gcLevel++; METER(if (rt->gcLevel > rt->gcStats.maxlevel) rt->gcStats.maxlevel = rt->gcLevel); if (!(gcflags & GC_ALREADY_LOCKED)) JS_UNLOCK_GC(rt); return; } /* * If we're in one or more requests (possibly on more than one context) * running on the current thread, indicate, temporarily, that all these * requests are inactive. NB: if cx->thread is 0, then cx is not using * the request model, and does not contribute to rt->requestCount. */ requestDebit = 0; if (cx->thread) { /* * Check all contexts for any with the same thread-id. XXX should we * keep a sub-list of contexts having the same id? */ iter = NULL; while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { if (acx->thread == cx->thread && acx->requestDepth) requestDebit++; } } else { /* * We assert, but check anyway, in case someone is misusing the API. * Avoiding the loop over all of rt's contexts is a win in the event * that the GC runs only on request-less contexts with 0 thread-ids, * in a special thread such as might be used by the UI/DOM/Layout * "mozilla" or "main" thread in Mozilla-the-browser. */ JS_ASSERT(cx->requestDepth == 0); if (cx->requestDepth) requestDebit = 1; } if (requestDebit) { JS_ASSERT(requestDebit <= rt->requestCount); rt->requestCount -= requestDebit; if (rt->requestCount == 0) JS_NOTIFY_REQUEST_DONE(rt); } /* If another thread is already in GC, don't attempt GC; wait instead. */ if (rt->gcLevel > 0) { /* Bump gcLevel to restart the current GC, so it finds new garbage. */ rt->gcLevel++; METER(if (rt->gcLevel > rt->gcStats.maxlevel) rt->gcStats.maxlevel = rt->gcLevel); /* Wait for the other thread to finish, then resume our request. */ while (rt->gcLevel > 0) JS_AWAIT_GC_DONE(rt); if (requestDebit) rt->requestCount += requestDebit; if (!(gcflags & GC_ALREADY_LOCKED)) JS_UNLOCK_GC(rt); return; } /* No other thread is in GC, so indicate that we're now in GC. */ rt->gcLevel = 1; rt->gcThread = currentThread; /* Wait for all other requests to finish. */ while (rt->requestCount > 0) JS_AWAIT_REQUEST_DONE(rt);#else /* !JS_THREADSAFE */ /* Bump gcLevel and return rather than nest; the outer gc will restart. */ rt->gcLevel++; METER(if (rt->gcLevel > rt->gcStats.maxlevel) rt->gcStats.maxlevel = rt->gcLevel); if (rt->gcLevel > 1) return;#endif /* !JS_THREADSAFE *///fprintf(stderr, "Doing the GC thing!\n"); /* FIXME */ /* * Set rt->gcRunning here within the GC lock, and after waiting for any * active requests to end, so that new requests that try to JS_AddRoot, * JS_RemoveRoot, or JS_RemoveRootRT block in JS_BeginRequest waiting for * rt->gcLevel to drop to zero, while request-less calls to the *Root* * APIs block in js_AddRoot or js_RemoveRoot (see above in this file), * waiting for GC to finish. */ rt->gcRunning = JS_TRUE; JS_UNLOCK_GC(rt); /* If a suspended compile is running on another context, keep atoms. */ if (rt->gcKeepAtoms) gcflags |= GC_KEEP_ATOMS; /* Reset malloc counter. */ rt->gcMallocBytes = 0; /* Drop atoms held by the property cache, and clear property weak links. */ js_DisablePropertyCache(cx); js_FlushPropertyCache(cx);#ifdef DEBUG_brendan { extern void js_DumpScopeMeters(JSRuntime *rt); js_DumpScopeMeters(rt); }#endifrestart: rt->gcNumber++; /* * Mark phase. */ JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_marker, cx); if (rt->gcLocksHash) JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_marker, cx); js_MarkAtomState(&rt->atomState, gcflags, gc_mark_atom_key_thing, cx); js_MarkWatchPoints(rt); iter = NULL; while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) { /* * Iterate frame chain and dormant chains. Temporarily tack current * frame onto the head of the dormant list to ease iteration. * * (NB: see comment on this whole "dormant" thing in js_Execute.) */ chain = acx->fp; if (chain) { JS_ASSERT(!chain->dormantNext); chain->dormantNext = acx->dormantFrameChain; } else { chain = acx->dormantFrameChain; } for (fp = chain; fp; fp = chain = chain->dormantNext) { do { if (fp->callobj) GC_MARK(cx, fp->callobj, "call object", NULL); if (fp->argsobj) GC_MARK(cx, fp->argsobj, "arguments object", NULL); if (fp->varobj) GC_MARK(cx, fp->varobj, "variables object", NULL); if (fp->script) js_MarkScript(cx, fp->script, NULL); if (fp->spbase) { /* * Don't mark what has not been pushed yet, or what * has been popped already. */ if (fp->script) { depth = fp->script->depth; nslots = (JS_UPTRDIFF(fp->sp, fp->spbase) < depth * sizeof(jsval)) ? (uintN)(fp->sp - fp->spbase) : depth; } else { nslots = (uintN) (fp->sp - fp->spbase); } GC_MARK_JSVALS(cx, nslots, fp->spbase, "operand"); } GC_MARK(cx, fp->thisp, "this", NULL); if (fp->argv) { nslots = fp->argc; if (fp->fun && fp->fun->nargs > nslots) nslots = fp->fun->nargs; GC_MARK_JSVALS(cx, nslots, fp->argv, "arg"); } if (JSVAL_IS_GCTHING(fp->rval)) GC_MARK(cx, JSVAL_TO_GCTHING(fp->rval), "rval", NULL); if (fp->vars) GC_MARK_JSVALS(cx, fp->nvars, fp->vars, "var"); GC_MARK(cx, fp->scopeChain, "scope chain", NULL); if (fp->sharpArray) GC_MARK(cx, fp->sharpArray, "sharp array", NULL); if (fp->objAtomMap) { JSAtom **vector, *atom; nslots = fp->objAtomMap->length; vector = fp->objAtomMap->vector; for (i = 0; i < nslots; i++) { atom = vector[i]; if (atom) GC_MARK_ATOM(cx, atom, NULL); } } } while ((fp = fp->down) != NULL); } /* Cleanup temporary "dormant" linkage. */ if (acx->fp) acx->fp->dormantNext = NULL; /* Mark other roots-by-definition in acx. */ GC_MARK(cx, acx->globalObject, "global object", NULL); GC_MARK(cx, acx->newborn[GCX_OBJECT], "newborn object", NULL); GC_MARK(cx, acx->newborn[GCX_STRING], "newborn string", NULL); GC_MARK(cx, acx->newborn[GCX_DOUBLE], "newborn double", NULL); GC_MARK(cx, acx->newborn[GCX_MUTABLE_STRING], "newborn mutable string", NULL); for (i = GCX_EXTERNAL_STRING; i < GCX_NTYPES; i++) GC_MARK(cx, acx->newborn[i], "newborn external string", NULL); if (acx->lastAtom) GC_MARK_ATOM(cx, acx->lastAtom, NULL);#if JS_HAS_EXCEPTIONS if (acx->throwing && JSVAL_IS_GCTHING(acx->exception)) GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception", NULL);#endif#if JS_HAS_LVALUE_RETURN if (acx->rval2set && JSVAL_IS_GCTHING(acx->rval2)) GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2", NULL);#endif for (sh = acx->stackHeaders; sh; sh = sh->down) { METER(rt->gcStats.stackseg++); METER(rt->gcStats.segslots += sh->nslots); GC_MARK_JSVALS(cx, sh->nslots, JS_STACK_SEGMENT(sh), "stack"); } } if (rt->gcCallback) (void) rt->gcCallback(cx, JSGC_MARK_END); /* * 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. */ js_SweepAtomState(&rt->atomState); js_SweepScopeProperties(rt); js_SweepScriptFilenames(rt); for (a = rt->gcArenaPool.first.next; a; a = a->next) { flagp = (uint8 *) a->base; split = (uint8 *) FIRST_THING_PAGE(a); limit = (JSGCThing *) a->avail; for (thing = (JSGCThing *) split; thing < limit; thing++) { if (((jsuword)thing & GC_PAGE_MASK) == 0) { flagp++; thing++; } flags = *flagp; if (flags & GCF_MARK) { *flagp &= ~GCF_MARK; } else if (!(flags & (GCF_LOCKMASK | GCF_FINAL))) { /* Call the finalizer with GCF_FINAL ORed into flags. */ type = flags & GCF_TYPEMASK; finalizer = gc_finalizers[type]; if (finalizer) { *flagp = (uint8)(flags | GCF_FINAL); if (type >= GCX_EXTERNAL_STRING) js_PurgeDeflatedStringCache((JSString *)thing); finalizer(cx, thing); } /* Set flags to GCF_FINAL, signifying that thing is free. */ *flagp = GCF_FINAL; JS_ASSERT(rt->gcBytes >= sizeof(JSGCThing) + sizeof(uint8)); rt->gcBytes -= sizeof(JSGCThing) + sizeof(uint8); } if (++flagp == split) flagp += GC_THINGS_SIZE; } } /* * Free phase. * Free any unused arenas and rebuild the JSGCThing freelist. */ ap = &rt->gcArenaPool.first.next; a = *ap; if (!a) goto out; all_clear = JS_TRUE; flp = oflp = &rt->gcFreeList; *flp = NULL; METER(rt->gcStats.freelen = 0); do { flagp = (uint8 *) a->base; split = (uint8 *) FIRST_THING_PAGE(a); limit = (JSGCThing *) a->avail; for (thing = (JSGCThing *) split; thing < limit; thing++) { if (((jsuword)thing & GC_PAGE_MASK) == 0) { flagp++; thing++; } if (*flagp != GCF_FINAL) { all_clear = JS_FALSE; } else { thing->flagp = flagp; *flp = thing; flp = &thing->next; METER(rt->gcStats.freelen++); } if (++flagp == split) flagp += GC_THINGS_SIZE; } if (all_clear) { JS_ARENA_DESTROY(&rt->gcArenaPool, a, ap); flp = oflp; METER(rt->gcStats.afree++); } else { ap = &a->next; all_clear = JS_TRUE; oflp = flp; } } while ((a = *ap) != NULL); /* Terminate the new freelist. */ *flp = NULL; if (rt->gcCallback) (void) rt->gcCallback(cx, JSGC_FINALIZE_END);#ifdef DEBUG_brendan { extern void DumpSrcNoteSizeHist(); DumpSrcNoteSizeHist(); }#endifout: JS_LOCK_GC(rt); if (rt->gcLevel > 1) { rt->gcLevel = 1; JS_UNLOCK_GC(rt); goto restart; } js_EnablePropertyCache(cx); rt->gcLevel = 0; rt->gcLastBytes = rt->gcBytes; rt->gcPoke = rt->gcRunning = JS_FALSE;#ifdef JS_THREADSAFE /* If we were invoked during a request, pay back the temporary debit. */ if (requestDebit) rt->requestCount += requestDebit; rt->gcThread = 0; JS_NOTIFY_GC_DONE(rt); if (!(gcflags & GC_ALREADY_LOCKED)) JS_UNLOCK_GC(rt);#endif if (rt->gcCallback) { if (gcflags & GC_ALREADY_LOCKED) JS_UNLOCK_GC(rt); (void) rt->gcCallback(cx, JSGC_END); if (gcflags & GC_ALREADY_LOCKED) JS_LOCK_GC(rt); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -