Bug 1662254 - Move incremental CC slice logic to CCGCScheduler r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D89989
This commit is contained in:
Steve Fink 2020-10-15 15:02:12 +00:00
Родитель 13a9ad9955
Коммит 0eb5a80f8f
4 изменённых файлов: 273 добавлений и 172 удалений

Просмотреть файл

@ -88,8 +88,14 @@ class CCGCScheduler {
TimeStamp GetLastCCEndTime() const { return mLastCCEndTime; }
bool IsEarlyForgetSkippable(uint32_t aN = kMajorForgetSkippableCalls) const {
return mCleanupsSinceLastGC < aN;
}
// State modification
void SetNeedsFullCC() { mNeedsFullCC = true; }
void NoteGCBegin() {
// Treat all GC as incremental here; non-incremental GC will just appear to
// be one slice.
@ -98,6 +104,7 @@ class CCGCScheduler {
void NoteGCEnd() {
mCCBlockStart = TimeStamp();
mCleanupsSinceLastGC = 0;
mInIncrementalGC = false;
}
@ -118,12 +125,23 @@ class CCGCScheduler {
void UnblockCC() { mCCBlockStart = TimeStamp(); }
void NoteForgetSkippableComplete(TimeStamp now) {
// Returns the number of purple buffer items that were processed and removed.
uint32_t NoteForgetSkippableComplete(
TimeStamp aNow, uint32_t aSuspectedBeforeForgetSkippable) {
mLastForgetSkippableEndTime = aNow;
uint32_t suspected = nsCycleCollector_suspectedCount();
mPreviousSuspectedCount = suspected;
mCleanupsSinceLastGC++;
return aSuspectedBeforeForgetSkippable - suspected;
}
void NoteCCEnd(TimeStamp when) { mLastCCEndTime = when; }
void NoteCCEnd(TimeStamp aWhen) {
mLastCCEndTime = aWhen;
mNeedsFullCC = false;
}
// The CC was abandoned without running a slice, so we only did forget
// skippables. Prevent running another cycle soon.
void NoteForgetSkippableOnlyCycle() {
mLastForgetSkippableCycleEndTime = TimeStamp::Now();
}
@ -196,12 +214,28 @@ class CCGCScheduler {
std::max({delaySliceBudget, laterSliceBudget, baseBudget}));
}
bool ShouldScheduleCC(uint32_t aCleanupsSinceLastGC) const {
TimeStamp now;
bool ShouldFireForgetSkippable(uint32_t aSuspected) const {
// Only do a forget skippable if there are more than a few new objects
// or we're doing the initial forget skippables.
return ((mPreviousSuspectedCount + 100) <= aSuspected) ||
mCleanupsSinceLastGC < kMajorForgetSkippableCalls;
}
// There is reason to suspect that there may be a significant amount of
// garbage to cycle collect: either we just finished a GC, or the purple
// buffer is getting really big, or it's getting somewhat big and it has been
// too long since the last CC.
bool IsCCNeeded(uint32_t aSuspected, TimeStamp aNow = TimeStamp::Now()) const {
return mNeedsFullCC || aSuspected > kCCPurpleLimit ||
(aSuspected > kCCForcedPurpleLimit && mLastCCEndTime &&
aNow - mLastCCEndTime > kCCForced);
}
bool ShouldScheduleCC() const {
TimeStamp now = TimeStamp::Now();
// Don't run consecutive CCs too often.
if (aCleanupsSinceLastGC && !mLastCCEndTime.IsNull()) {
now = TimeStamp::Now();
if (mCleanupsSinceLastGC && !mLastCCEndTime.IsNull()) {
if (now - mLastCCEndTime < kCCDelay) {
return false;
}
@ -209,18 +243,181 @@ class CCGCScheduler {
// If GC hasn't run recently and forget skippable only cycle was run,
// don't start a new cycle too soon.
if ((aCleanupsSinceLastGC > kMajorForgetSkippableCalls) &&
if ((mCleanupsSinceLastGC > kMajorForgetSkippableCalls) &&
!mLastForgetSkippableCycleEndTime.IsNull()) {
if (now.IsNull()) {
now = TimeStamp::Now();
}
if (now - mLastForgetSkippableCycleEndTime <
kTimeBetweenForgetSkippableCycles) {
return false;
}
}
return true;
return IsCCNeeded(nsCycleCollector_suspectedCount(), now);
}
bool IsLastEarlyCCTimer(int32_t aCurrentFireCount) {
int32_t numEarlyTimerFires =
std::max(int32_t(mCCDelay / kCCSkippableDelay) - 2, 1);
return aCurrentFireCount >= numEarlyTimerFires;
}
enum class CCRunnerAction {
None,
ForgetSkippable,
PrepForCC,
CycleCollect,
StopRunning
};
enum class CCRunnerState {
Inactive,
EarlyTimer,
LateTimer,
LateTimerPostForgetSkippable,
FinalTimer
};
enum CCRunnerYield { Continue, Yield };
enum CCRunnerForgetSkippableRemoveChildless {
KeepChildless = false,
RemoveChildless = true
};
struct CCRunnerStep {
// The action to scheduler is instructing the caller to perform.
CCRunnerAction mAction;
// Whether to stop processing actions for this invocation of the timer
// callback.
CCRunnerYield mYield;
// If the action is ForgetSkippable, then whether to remove childless nodes
// or not. (ForgetSkippable is the only action requiring a parameter; if
// that changes, this will become a union.)
CCRunnerForgetSkippableRemoveChildless mRemoveChildless;
};
void ActivateCCRunner() {
MOZ_ASSERT(mCCRunnerState == CCRunnerState::Inactive);
mCCRunnerState = CCRunnerState::EarlyTimer;
mCCDelay = kCCDelay;
mCCRunnerEarlyFireCount = 0;
}
void DeactivateCCRunner() { mCCRunnerState = CCRunnerState::Inactive; }
CCRunnerStep GetNextCCRunnerAction(TimeStamp aDeadline, uint32_t aSuspected) {
if (mCCRunnerState == CCRunnerState::Inactive) {
// When we cancel a cycle, there may have been a final ForgetSkippable.
return {CCRunnerAction::StopRunning, Yield};
}
TimeStamp now = TimeStamp::Now();
if (InIncrementalGC()) {
if (EnsureCCIsBlocked(now) == StartingLockout) {
// Reset our state so that we run forgetSkippable often enough before
// CC. Because of reduced mCCDelay forgetSkippable will be called just
// a few times. kMaxCCLockedoutTime limit guarantees that we end up
// calling forgetSkippable and CycleCollectNow eventually.
mCCRunnerState = CCRunnerState::EarlyTimer;
mCCRunnerEarlyFireCount = 0;
mCCDelay = kCCDelay / int64_t(3);
return {CCRunnerAction::None, Yield};
}
if (GetCCBlockedTime(now).value() < kMaxCCLockedoutTime) {
return {CCRunnerAction::None, Yield};
}
// Locked out for too long, so proceed and finish the incremental GC
// synchronously.
}
switch (mCCRunnerState) {
// EarlyTimer: a GC ran (or we otherwise decided to try CC'ing). Wait for
// some amount of time (kCCDelay, or less if incremental GC blocked this
// CC) while firing regular ForgetSkippable actions before continuing on.
case CCRunnerState::EarlyTimer:
++mCCRunnerEarlyFireCount;
if (IsLastEarlyCCTimer(mCCRunnerEarlyFireCount)) {
mCCRunnerState = CCRunnerState::LateTimer;
}
if (ShouldFireForgetSkippable(aSuspected)) {
return {CCRunnerAction::ForgetSkippable, Yield, KeepChildless};
}
if (aDeadline.IsNull()) {
return {CCRunnerAction::None, Yield};
}
// If we're called during idle time, try to find some work to do by
// advancing to the next state, effectively bypassing some possible
// forget skippable calls.
mCCRunnerState = CCRunnerState::LateTimer;
[[fallthrough]];
// LateTimer: do a stronger ForgetSkippable that removes nodes with no
// children in the cycle collector graph. This state is split into two
// parts; LateTimerPostForgetSkippable will happen within the same
// callback (unless the ForgetSkippable shrinks the purple buffer enough
// for the CC to be skipped entirely.)
case CCRunnerState::LateTimer:
if (!IsCCNeeded(aSuspected, now)) {
mCCRunnerState = CCRunnerState::Inactive;
NoteForgetSkippableOnlyCycle();
if (ShouldFireForgetSkippable(aSuspected)) {
return {CCRunnerAction::ForgetSkippable, Yield, KeepChildless};
}
return {CCRunnerAction::StopRunning, Yield};
}
mCCRunnerState = CCRunnerState::LateTimerPostForgetSkippable;
return {CCRunnerAction::ForgetSkippable, Yield, RemoveChildless};
// LateTimerPostForgetSkippable: continuing LateTimer, possibly do some
// final setup before the actual cycle collection slice.
case CCRunnerState::LateTimerPostForgetSkippable:
if (!IsCCNeeded(aSuspected, now)) {
mCCRunnerState = CCRunnerState::Inactive;
NoteForgetSkippableOnlyCycle();
return {CCRunnerAction::StopRunning, Yield};
}
// Our efforts to avoid a CC have failed. Let the timer fire once more
// to trigger a CC.
mCCRunnerState = CCRunnerState::FinalTimer;
if (!aDeadline.IsNull() && TimeStamp::Now() < aDeadline) {
return {CCRunnerAction::PrepForCC, Yield};
}
[[fallthrough]];
// FinalTimer: the final state where we actually do a slice of cycle
// collection and reset the timer.
case CCRunnerState::FinalTimer:
if (!IsCCNeeded(aSuspected, now)) {
mCCRunnerState = CCRunnerState::Inactive;
NoteForgetSkippableOnlyCycle();
if (ShouldFireForgetSkippable(aSuspected)) {
return {CCRunnerAction::ForgetSkippable, Yield, KeepChildless};
}
return {CCRunnerAction::StopRunning, Yield};
}
// We are in the final timer fire and still meet the conditions for
// triggering a CC. Let RunCycleCollectorSlice finish the current IGC
// if any, because that will allow us to include the GC time in the CC
// pause.
mCCRunnerState = CCRunnerState::Inactive;
return {CCRunnerAction::CycleCollect, Yield};
default:
MOZ_CRASH("Unexpected CCRunner state");
};
}
// aStartTimeStamp : when the ForgetSkippable timer fired. This may be some
@ -273,6 +470,14 @@ class CCGCScheduler {
TimeStamp mLastCCEndTime;
TimeStamp mLastForgetSkippableCycleEndTime;
CCRunnerState mCCRunnerState = CCRunnerState::Inactive;
int32_t mCCRunnerEarlyFireCount = 0;
TimeDuration mCCDelay = kCCDelay;
bool mNeedsFullCC = false;
uint32_t mPreviousSuspectedCount = 0;
uint32_t mCleanupsSinceLastGC = UINT32_MAX;
// Configuration parameters
TimeDuration mActiveIntersliceGCBudget = TimeDuration::FromMilliseconds(5);

Просмотреть файл

@ -315,8 +315,9 @@ nsresult nsCCUncollectableMarker::Observe(nsISupports* aSubject,
!strcmp(aTopic, "cycle-collector-forget-skippable"),
"wrong topic");
// JS cleanup can be slow. Do it only if there has been a GC.
const bool cleanupJS = nsJSContext::CleanupsSinceLastGC() == 0 &&
// JS cleanup can be slow. Do it only if this is the first forget-skippable
// after a GC.
const bool cleanupJS = nsJSContext::HasHadCleanupSinceLastGC() &&
!strcmp(aTopic, "cycle-collector-forget-skippable");
const bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin");

Просмотреть файл

@ -89,8 +89,6 @@ using namespace mozilla::dom;
# undef CompareString
#endif
enum class CCRunnerState { Inactive, EarlyTimer, LateTimer, FinalTimer };
static nsITimer* sGCTimer;
static nsITimer* sShrinkingGCTimer;
static StaticRefPtr<IdleTaskRunner> sCCRunner;
@ -100,9 +98,6 @@ static StaticRefPtr<IdleTaskRunner> sInterSliceGCRunner;
static TimeStamp sCurrentGCStartTime;
static CCRunnerState sCCRunnerState = CCRunnerState::Inactive;
static TimeDuration sCCDelay = kCCDelay;
static JS::GCSliceCallback sPrevGCSliceCallback;
static bool sHasRunGC;
@ -110,10 +105,6 @@ static bool sHasRunGC;
static uint32_t sCCollectedWaitingForGC;
static uint32_t sCCollectedZonesWaitingForGC;
static uint32_t sLikelyShortLivingObjectsNeedingGC;
static int32_t sCCRunnerEarlyFireCount = 0;
static uint32_t sPreviousSuspectedCount = 0;
static uint32_t sCleanupsSinceLastGC = UINT32_MAX;
static bool sNeedsFullCC = false;
static bool sNeedsFullGC = false;
static bool sNeedsGCAfterCC = false;
static bool sIncrementalCC = false;
@ -1157,18 +1148,15 @@ static void FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless,
js::SliceBudget budget =
sScheduler.ComputeForgetSkippableBudget(startTimeStamp, aDeadline);
bool earlyForgetSkippable = sCleanupsSinceLastGC < kMajorForgetSkippableCalls;
bool earlyForgetSkippable = sScheduler.IsEarlyForgetSkippable();
nsCycleCollector_forgetSkippable(budget, aRemoveChildless,
earlyForgetSkippable);
TimeStamp now = TimeStamp::Now();
sScheduler.NoteForgetSkippableComplete(now);
sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
++sCleanupsSinceLastGC;
uint32_t removedPurples =
sScheduler.NoteForgetSkippableComplete(now, aSuspected);
TimeDuration duration = now - startTimeStamp;
uint32_t removedPurples = aSuspected - sPreviousSuspectedCount;
sCCStats.AfterForgetSkippable(duration, removedPurples);
if (duration.ToSeconds()) {
@ -1527,8 +1515,8 @@ void nsJSContext::BeginCycleCollectionCallback() {
// Run forgetSkippable synchronously to reduce the size of the CC graph. This
// is particularly useful if we recently finished a GC.
if (sCleanupsSinceLastGC < kMajorForgetSkippableCalls) {
while (sCleanupsSinceLastGC < kMajorForgetSkippableCalls) {
if (sScheduler.IsEarlyForgetSkippable()) {
while (sScheduler.IsEarlyForgetSkippable()) {
FireForgetSkippable(nsCycleCollector_suspectedCount(), false,
TimeStamp());
}
@ -1590,7 +1578,6 @@ void nsJSContext::EndCycleCollectionCallback(CycleCollectorResults& aResults) {
// Update global state to indicate we have just run a cycle collection.
sScheduler.NoteCCEnd(endCCTimeStamp);
sNeedsFullCC = false;
sNeedsGCAfterCC = false;
sCCStats.Clear();
}
@ -1668,161 +1655,72 @@ void ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure) {
nsJSContext::ShrinkingGC);
}
static bool ShouldTriggerCC(uint32_t aSuspected) {
return sNeedsFullCC || aSuspected > kCCPurpleLimit ||
(aSuspected > kCCForcedPurpleLimit &&
TimeUntilNow(sScheduler.GetLastCCEndTime()) > kCCForced);
}
static inline bool ShouldFireForgetSkippable(uint32_t aSuspected) {
// Only do a forget skippable if there are more than a few new objects
// or we're doing the initial forget skippables.
return ((sPreviousSuspectedCount + 100) <= aSuspected) ||
sCleanupsSinceLastGC < kMajorForgetSkippableCalls;
}
static inline bool IsLastEarlyCCTimer(int32_t aCurrentFireCount) {
int32_t numEarlyTimerFires =
std::max(int32_t(sCCDelay / kCCSkippableDelay) - 2, 1);
return aCurrentFireCount >= numEarlyTimerFires;
}
static void ActivateCCRunner() {
MOZ_ASSERT(sCCRunnerState == CCRunnerState::Inactive);
sCCRunnerState = CCRunnerState::EarlyTimer;
sCCDelay = kCCDelay;
sCCRunnerEarlyFireCount = 0;
}
static bool CCRunnerFired(TimeStamp aDeadline) {
if (sDidShutdown) {
return false;
}
if (sScheduler.InIncrementalGC()) {
TimeStamp now = TimeStamp::Now();
if (sScheduler.EnsureCCIsBlocked(now) == sScheduler.StartingLockout) {
// Reset our state so that we run forgetSkippable often enough before
// CC. Because of reduced sCCDelay forgetSkippable will be called just a
// few times. kMaxCCLockedoutTime limit guarantees that we end up calling
// forgetSkippable and CycleCollectNow eventually.
sCCRunnerState = CCRunnerState::EarlyTimer;
sCCRunnerEarlyFireCount = 0;
sCCDelay = kCCDelay / int64_t(3);
return false;
}
if (sScheduler.GetCCBlockedTime(now).value() < kMaxCCLockedoutTime) {
return false;
}
}
bool didDoWork = false;
// Either we have run a CC slice, or we have decided no CC is needed.
bool finished = false;
using CCRunnerAction = CCGCScheduler::CCRunnerAction;
using CCRunnerStep = CCGCScheduler::CCRunnerStep;
uint32_t suspected = nsCycleCollector_suspectedCount();
switch (sCCRunnerState) {
case CCRunnerState::EarlyTimer:
++sCCRunnerEarlyFireCount;
if (IsLastEarlyCCTimer(sCCRunnerEarlyFireCount)) {
sCCRunnerState = CCRunnerState::LateTimer;
}
if (ShouldFireForgetSkippable(suspected)) {
FireForgetSkippable(suspected, /* aRemoveChildless = */ false,
aDeadline);
didDoWork = true;
// The CC/GC scheduler (sScheduler) decides what action(s) to take during
// this invocation of the CC runner.
//
// This may be zero, one, or multiple actions. (Zero is when CC is blocked by
// incremental GC, or when the scheduler determined that a CC is no longer
// needed.) Loop until an action is requested that finishes this invocation,
// or the scheduler decides that this invocation should finish by returning
// `Yield`.
CCRunnerStep step;
do {
uint32_t suspected = nsCycleCollector_suspectedCount();
step = sScheduler.GetNextCCRunnerAction(aDeadline, suspected);
switch (step.mAction) {
case CCRunnerAction::None:
break;
}
if (aDeadline.IsNull()) {
case CCRunnerAction::ForgetSkippable:
// 'Forget skippable' only, then end this invocation.
FireForgetSkippable(suspected, bool(step.mRemoveChildless), aDeadline);
break;
}
// If we're called during idle time, try to find some work to do by
// advancing to the next state, effectively bypassing some possible forget
// skippable calls.
MOZ_ASSERT(!didDoWork);
sCCRunnerState = CCRunnerState::LateTimer;
[[fallthrough]];
case CCRunnerState::LateTimer:
if (!ShouldTriggerCC(suspected)) {
if (ShouldFireForgetSkippable(suspected)) {
FireForgetSkippable(suspected, /* aRemoveChildless = */ false,
aDeadline);
didDoWork = true;
}
finished = true;
break;
}
FireForgetSkippable(suspected, /* aRemoveChildless = */ true, aDeadline);
didDoWork = true;
if (!ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
finished = true;
break;
}
// Our efforts to avoid a CC have failed, so we return to let the
// timer fire once more to trigger a CC.
sCCRunnerState = CCRunnerState::FinalTimer;
if (!aDeadline.IsNull() && TimeStamp::Now() < aDeadline) {
// Clear content unbinder before the first CC slice.
case CCRunnerAction::PrepForCC:
// Clear content unbinder before the first actual CC slice.
Element::ClearContentUnbinder();
if (TimeStamp::Now() < aDeadline) {
// And trigger deferred deletion too.
// PrepForCC will only be requested when idle. If we still have time
// left before the deadline, trigger deferred deletion too.
nsCycleCollector_doDeferredDeletion();
}
}
break;
case CCRunnerState::FinalTimer:
if (!ShouldTriggerCC(suspected)) {
if (ShouldFireForgetSkippable(suspected)) {
FireForgetSkippable(suspected, /* aRemoveChildless = */ false,
aDeadline);
didDoWork = true;
}
finished = true;
break;
}
// We are in the final timer fire and still meet the conditions for
// triggering a CC. Let RunCycleCollectorSlice finish the current IGC if
// any, because that will allow us to include the GC time in the CC pause.
nsJSContext::RunCycleCollectorSlice(aDeadline);
didDoWork = true;
finished = true;
break;
case CCRunnerAction::CycleCollect:
// Cycle collection slice.
nsJSContext::RunCycleCollectorSlice(aDeadline);
break;
default:
MOZ_CRASH("Unexpected CCRunner state");
}
if (finished) {
sPreviousSuspectedCount = 0;
nsJSContext::KillCCRunner();
if (!didDoWork) {
// The CC was abandoned without running a slice, so we only did forget
// skippables. Prevent running another cycle soon.
sScheduler.NoteForgetSkippableOnlyCycle();
case CCRunnerAction::StopRunning:
// End this CC, either because we have run a cycle collection slice, or
// because a CC is no longer needed.
nsJSContext::KillCCRunner();
break;
}
}
if (step.mAction != CCRunnerAction::None) {
didDoWork = true;
}
} while (step.mYield == CCGCScheduler::CCRunnerYield::Continue);
return didDoWork;
}
// static
uint32_t nsJSContext::CleanupsSinceLastGC() { return sCleanupsSinceLastGC; }
bool nsJSContext::HasHadCleanupSinceLastGC() {
return sScheduler.IsEarlyForgetSkippable(1);
}
// Check all of the various collector timers/runners and see if they are waiting
// to fire. This does not check sFullGCTimer, as that's a more expensive
@ -1952,7 +1850,7 @@ void nsJSContext::PokeGC(JS::GCReason aReason, JSObject* aObj,
if (sCCRunner) {
// Make sure CC is called...
sNeedsFullCC = true;
sScheduler.SetNeedsFullCC();
// and GC after it.
sNeedsGCAfterCC = true;
return;
@ -1960,7 +1858,7 @@ void nsJSContext::PokeGC(JS::GCReason aReason, JSObject* aObj,
if (sICCRunner) {
// Make sure GC is called after the current CC completes.
// No need to set sNeedsFullCC because we are currently running a CC.
// No need to SetNeedsFullCC because we are currently running a CC.
sNeedsGCAfterCC = true;
return;
}
@ -1995,12 +1893,11 @@ void nsJSContext::MaybePokeCC() {
return;
}
if (sScheduler.ShouldScheduleCC(sCleanupsSinceLastGC) &&
ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
if (sScheduler.ShouldScheduleCC()) {
// We can kill some objects before running forgetSkippable.
nsCycleCollector_dispatchDeferredDeletion();
ActivateCCRunner();
sScheduler.ActivateCCRunner();
sCCRunner =
IdleTaskRunner::Create(CCRunnerFired, "MaybePokeCC::CCRunnerFired",
kCCSkippableDelay.ToMilliseconds(),
@ -2042,7 +1939,7 @@ void nsJSContext::KillShrinkingGCTimer() {
// static
void nsJSContext::KillCCRunner() {
sScheduler.UnblockCC();
sCCRunnerState = CCRunnerState::Inactive;
sScheduler.DeactivateCCRunner();
if (sCCRunner) {
sCCRunner->Cancel();
sCCRunner = nullptr;
@ -2098,8 +1995,7 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
sCCollectedWaitingForGC = 0;
sCCollectedZonesWaitingForGC = 0;
sLikelyShortLivingObjectsNeedingGC = 0;
sCleanupsSinceLastGC = 0;
sNeedsFullCC = true;
sScheduler.SetNeedsFullCC();
sHasRunGC = true;
nsJSContext::MaybePokeCC();
@ -2114,7 +2010,7 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
nsJSContext::KillFullGCTimer();
}
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
if (sScheduler.IsCCNeeded(nsCycleCollector_suspectedCount())) {
nsCycleCollector_dispatchDeferredDeletion();
}
@ -2150,7 +2046,7 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
[] { return sShuttingDown; });
}
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
if (sScheduler.IsCCNeeded(nsCycleCollector_suspectedCount())) {
nsCycleCollector_dispatchDeferredDeletion();
}
@ -2196,7 +2092,6 @@ void mozilla::dom::StartupJSEnvironment() {
sCCollectedWaitingForGC = 0;
sCCollectedZonesWaitingForGC = 0;
sLikelyShortLivingObjectsNeedingGC = 0;
sNeedsFullCC = false;
sNeedsFullGC = true;
sNeedsGCAfterCC = false;
sIsInitialized = false;

Просмотреть файл

@ -116,7 +116,7 @@ class nsJSContext : public nsIScriptContext {
// Calling LikelyShortLivingObjectCreated() makes a GC more likely.
static void LikelyShortLivingObjectCreated();
static uint32_t CleanupsSinceLastGC();
static bool HasHadCleanupSinceLastGC();
nsIScriptGlobalObject* GetCachedGlobalObject() {
// Verify that we have a global so that this