/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// The amount of time we wait between a request to CC (after GC ran) // and doing the actual CC. externconst TimeDuration kCCDelay;
externconst TimeDuration kCCSkippableDelay;
// In case the cycle collector isn't run at all, we don't want forget skippables // to run too often. So limit the forget skippable cycle to start at earliest 2 // seconds after the end of the previous cycle. externconst TimeDuration kTimeBetweenForgetSkippableCycles;
// ForgetSkippable is usually fast, so we can use small budgets. // This isn't a real budget but a hint to IdleTaskRunner whether there // is enough time to call ForgetSkippable. externconst TimeDuration kForgetSkippableSliceDuration;
// Maximum amount of time that should elapse between incremental CC slices externconst TimeDuration kICCIntersliceDelay;
// Time budget for an incremental CC slice when using timer to run it. externconst TimeDuration kICCSliceBudget; // Minimum budget for an incremental CC slice when using idle time to run it. externconst TimeDuration kIdleICCSliceBudget;
// Maximum total duration for an ICC externconst TimeDuration kMaxICCDuration;
// Force a CC after this long if there's more than NS_CC_FORCED_PURPLE_LIMIT // objects in the purple buffer. externconst TimeDuration kCCForced;
constexpr uint32_t kCCForcedPurpleLimit = 10;
// Don't allow an incremental GC to lock out the CC for too long. externconst TimeDuration kMaxCCLockedoutTime;
// Trigger a CC if the purple buffer exceeds this size when we check it.
constexpr uint32_t kCCPurpleLimit = 200;
// Actions performed by the GCRunner state machine. enumclass GCRunnerAction {
MinorGC, // Run a minor GC (nursery collection)
WaitToMajorGC, // We want to start a new major GC
StartMajorGC, // The parent says we may begin our major GC
GCSlice, // Run a single slice of a major GC
None
};
struct CCRunnerStep { // The action the scheduler is instructing the caller to perform.
CCRunnerAction mAction;
// Whether to stop processing actions for this invocation of the timer // callback.
CCRunnerYield mYield;
union ActionData { // If the action is ForgetSkippable, then whether to remove childless nodes // or not.
CCRunnerForgetSkippableRemoveChildless mRemoveChildless;
// If the action is CycleCollect, the reason for the collection.
CCReason mCCReason;
// If the action is MinorGC, the reason for the GC.
JS::GCReason mReason;
JS::SliceBudget CreateGCSliceBudget(mozilla::TimeDuration aDuration, bool isIdle, bool isExtended) {
mInterruptRequested = false; // Don't try to interrupt if we are in a mode where idle time is rare. auto budget = JS::SliceBudget(
aDuration, mPreferFasterCollection ? nullptr : &mInterruptRequested);
budget.idle = isIdle;
budget.extended = isExtended; return budget;
}
/* * aDelay is the delay before the first time the idle task runner runs. * Then it runs every * StaticPrefs::javascript_options_gc_delay_interslice()
*/ void EnsureGCRunner(TimeDuration aDelay);
// If GCRunner isn't active, this calls EnsureGCRunner(0). Otherwise the timer // is reset. void EnsureOrResetGCRunner();
// If the GC being requested is not a shrinking GC set this flag. // If/when the shrinking GC timer fires but the user is active we check // this flag before canceling the GC, so as not to cancel the // non-shrinking GC being requested here. if (aReason != JS::GCReason::USER_INACTIVE) {
mWantAtLeastRegularGC = true;
}
// Force full GCs when called from reftests so that we collect dead zones // that have not been scheduled for collection. if (aReason == JS::GCReason::DOM_WINDOW_UTILS) {
SetNeedsFullGC();
}
// USER_INACTIVE trumps everything, // FULL_GC_TIMER trumps everything except USER_INACTIVE, // all other reasons just use the latest reason. switch (aReason) { case JS::GCReason::USER_INACTIVE:
mMajorGCReason = aReason; break; case JS::GCReason::FULL_GC_TIMER: if (mMajorGCReason != JS::GCReason::USER_INACTIVE) {
mMajorGCReason = aReason;
} break; default: if (mMajorGCReason != JS::GCReason::USER_INACTIVE &&
mMajorGCReason != JS::GCReason::FULL_GC_TIMER) {
mMajorGCReason = aReason;
} break;
}
}
// Ensure that the current runner does a cycle collection, and trigger a GC // after it finishes. void EnsureCCThenGC(CCReason aReason) {
MOZ_ASSERT(mCCRunnerState != CCRunnerState::Inactive);
MOZ_ASSERT(aReason != CCReason::NO_REASON);
mNeedsFullCC = aReason;
mNeedsGCAfterCC = true;
}
// Returns false if we started and finished a major GC while waiting for a // response.
[[nodiscard]] bool NoteReadyForMajorGC() { if (mMajorGCReason == JS::GCReason::NO_REASON || InIncrementalGC()) { returnfalse;
}
mReadyForMajorGC = true; returntrue;
}
// Starting a major GC (incremental or non-incremental). void NoteGCBegin(JS::GCReason aReason);
// Major GC completed. void NoteGCEnd();
// A timer fired, but then decided not to run a GC. void NoteWontGC();
// This is invoked when we reach the actual cycle collection portion of the // overall cycle collection. void NoteCCBegin();
// This is invoked when the whole process of collection is done -- i.e., CC // preparation (eg ForgetSkippables) in addition to the CC itself. There // really ought to be a separate name for the overall CC as opposed to the // actual cycle collection portion. void NoteCCEnd(const CycleCollectorResults& aResults, TimeStamp aWhen);
// A single slice has completed. void NoteGCSliceEnd(TimeStamp aStart, TimeStamp aEnd);
using MayGCPromise =
MozPromise<bool, mozilla::ipc::ResponseRejectReason, true>;
// Returns null if we shouldn't GC now (eg a GC is already running). static RefPtr<MayGCPromise> MayGCNow(JS::GCReason reason);
// Check all of the various collector timers/runners and see if they are // waiting to fire. This does not check the Full GC Timer, as that's a // more expensive collection we run on a long timer. void RunNextCollectorTimer(JS::GCReason aReason,
mozilla::TimeStamp aDeadline);
// When we decide to do a cycle collection but we're in the middle of an // incremental GC, the CC is "locked out" until the GC completes -- unless // the wait is too long, and we decide to finish the incremental GC early. void BlockCC(TimeStamp aNow) {
MOZ_ASSERT(mInIncrementalGC);
MOZ_ASSERT(mCCBlockStart.IsNull());
mCCBlockStart = aNow;
}
void UnblockCC() { mCCBlockStart = TimeStamp(); }
// Returns the number of purple buffer items that were processed and removed. void NoteForgetSkippableComplete(TimeStamp aNow,
uint32_t aSuspectedCCObjects) {
mLastForgetSkippableEndTime = aNow;
mPreviousSuspectedCount = aSuspectedCCObjects;
mCleanupsSinceLastGC++;
}
// Test if we are in the NoteCCBegin .. NoteCCEnd interval. bool IsCollectingCycles() const { return mIsCollectingCycles; }
// The CC was abandoned without running a slice, so we only did forget // skippables. Prevent running another cycle soon. void NoteForgetSkippableOnlyCycle(TimeStamp aNow) {
mLastForgetSkippableCycleEndTime = aNow;
}
// Return a budget along with a boolean saying whether to prefer to run short // slices and stop rather than continuing to the next phase of cycle // collection.
JS::SliceBudget ComputeCCSliceBudget(TimeStamp aDeadline,
TimeStamp aCCBeginTime,
TimeStamp aPrevSliceEndTime,
TimeStamp aNow, bool* aPreferShorterSlices) const;
bool ShouldForgetSkippable(uint32_t aSuspectedCCObjects) 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) <= aSuspectedCCObjects) ||
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.
CCReason IsCCNeeded(TimeStamp aNow, uint32_t aSuspectedCCObjects) const { if (mNeedsFullCC != CCReason::NO_REASON) { return mNeedsFullCC;
} if (aSuspectedCCObjects > kCCPurpleLimit) { return CCReason::MANY_SUSPECTED;
} if (aSuspectedCCObjects > kCCForcedPurpleLimit && mLastCCEndTime &&
aNow - mLastCCEndTime > kCCForced) { return CCReason::TIMED;
} return CCReason::NO_REASON;
}
// If we collected a substantial amount of cycles, poke the GC since more // objects might be unreachable now. bool NeedsGCAfterCC() const { return mCCollectedWaitingForGC > 250 || mCCollectedZonesWaitingForGC > 0 ||
mLikelyShortLivingObjectsNeedingGC > 2500 || mNeedsGCAfterCC;
}
// The state machine should always have been deactivated after the previous // collection, however far that collection may have gone.
MOZ_ASSERT(mCCRunnerState == CCRunnerState::Inactive, "DeactivateCCRunner should have been called");
mCCRunnerState = initialState;
// Currently, there are only two entry points to the non-Inactive part of // the state machine. if (initialState == CCRunnerState::ReducePurple) {
mCCDelay = kCCDelay;
mCCRunnerEarlyFireCount = 0;
} elseif (initialState == CCRunnerState::CycleCollecting) { // Nothing needed.
} else {
MOZ_CRASH("Invalid initial state");
}
}
// aStartTimeStamp : when the ForgetSkippable timer fired. This may be some // time ago, if an incremental GC needed to be finished.
JS::SliceBudget ComputeForgetSkippableBudget(TimeStamp aStartTimeStamp,
TimeStamp aDeadline);
// An incremental GC is in progress, which blocks the CC from running for its // duration (or until it goes too long and is finished synchronously.) bool mInIncrementalGC = false;
// Whether to ask the parent process if now is a good time to GC (false for // the parent process.) constbool mAskParentBeforeMajorGC;
// We've asked the parent process if now is a good time to GC (do not ask // again). bool mHaveAskedParent = false;
// The parent process is ready for us to do a major GC. bool mReadyForMajorGC;
// Set when the IdleTaskRunner requests the current task be interrupted. // Cleared when the GC slice budget has detected the interrupt request.
JS::SliceBudget::InterruptRequestFlag mInterruptRequested;
// When a shrinking GC has been requested but we back-out, if this is true // we run a non-shrinking GC. bool mWantAtLeastRegularGC = false;
// When the CC started actually waiting for the GC to finish. This will be // set to non-null at a later time than mCCLockedOut.
TimeStamp mCCBlockStart;
// If the GC runner triggers a GC slice, this will be set to the idle deadline // or the null timestamp if non-idle. It will be Nothing at the end of an // internally-triggered slice.
mozilla::Maybe<TimeStamp> mTriggeredGCDeadline;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.