const gcGoalUtilization … const gcBackgroundUtilization … const gcCreditSlack … const gcAssistTimeSlack … const gcOverAssistWork … const defaultHeapMinimum … const maxStackScanSlack … const memoryLimitMinHeapGoalHeadroom … const memoryLimitHeapGoalHeadroomPercent … var gcController … type gcControllerState … func (c *gcControllerState) init(gcPercent int32, memoryLimit int64) { … } // startCycle resets the GC controller's state and computes estimates // for a new GC cycle. The caller must hold worldsema and the world // must be stopped. func (c *gcControllerState) startCycle(markStartTime int64, procs int, trigger gcTrigger) { … } // revise updates the assist ratio during the GC cycle to account for // improved estimates. This should be called whenever gcController.heapScan, // gcController.heapLive, or if any inputs to gcController.heapGoal are // updated. It is safe to call concurrently, but it may race with other // calls to revise. // // The result of this race is that the two assist ratio values may not line // up or may be stale. In practice this is OK because the assist ratio // moves slowly throughout a GC cycle, and the assist ratio is a best-effort // heuristic anyway. Furthermore, no part of the heuristic depends on // the two assist ratio values being exact reciprocals of one another, since // the two values are used to convert values from different sources. // // The worst case result of this raciness is that we may miss a larger shift // in the ratio (say, if we decide to pace more aggressively against the // hard heap goal) but even this "hard goal" is best-effort (see #40460). // The dedicated GC should ensure we don't exceed the hard goal by too much // in the rare case we do exceed it. // // It should only be called when gcBlackenEnabled != 0 (because this // is when assists are enabled and the necessary statistics are // available). func (c *gcControllerState) revise() { … } // endCycle computes the consMark estimate for the next cycle. // userForced indicates whether the current GC cycle was forced // by the application. func (c *gcControllerState) endCycle(now int64, procs int, userForced bool) { … } // enlistWorker encourages another dedicated mark worker to start on // another P if there are spare worker slots. It is used by putfull // when more work is made available. // //go:nowritebarrier func (c *gcControllerState) enlistWorker() { … } // findRunnableGCWorker returns a background mark worker for pp if it // should be run. This must only be called when gcBlackenEnabled != 0. func (c *gcControllerState) findRunnableGCWorker(pp *p, now int64) (*g, int64) { … } // resetLive sets up the controller state for the next mark phase after the end // of the previous one. Must be called after endCycle and before commit, before // the world is started. // // The world must be stopped. func (c *gcControllerState) resetLive(bytesMarked uint64) { … } // markWorkerStop must be called whenever a mark worker stops executing. // // It updates mark work accounting in the controller by a duration of // work in nanoseconds and other bookkeeping. // // Safe to execute at any time. func (c *gcControllerState) markWorkerStop(mode gcMarkWorkerMode, duration int64) { … } func (c *gcControllerState) update(dHeapLive, dHeapScan int64) { … } func (c *gcControllerState) addScannableStack(pp *p, amount int64) { … } func (c *gcControllerState) addGlobals(amount int64) { … } // heapGoal returns the current heap goal. func (c *gcControllerState) heapGoal() uint64 { … } // heapGoalInternal is the implementation of heapGoal which returns additional // information that is necessary for computing the trigger. // // The returned minTrigger is always <= goal. func (c *gcControllerState) heapGoalInternal() (goal, minTrigger uint64) { … } // memoryLimitHeapGoal returns a heap goal derived from memoryLimit. func (c *gcControllerState) memoryLimitHeapGoal() uint64 { … } const triggerRatioDen … const minTriggerRatioNum … const maxTriggerRatioNum … // trigger returns the current point at which a GC should trigger along with // the heap goal. // // The returned value may be compared against heapLive to determine whether // the GC should trigger. Thus, the GC trigger condition should be (but may // not be, in the case of small movements for efficiency) checked whenever // the heap goal may change. func (c *gcControllerState) trigger() (uint64, uint64) { … } // commit recomputes all pacing parameters needed to derive the // trigger and the heap goal. Namely, the gcPercent-based heap goal, // and the amount of runway we want to give the GC this cycle. // // This can be called any time. If GC is the in the middle of a // concurrent phase, it will adjust the pacing of that phase. // // isSweepDone should be the result of calling isSweepDone(), // unless we're testing or we know we're executing during a GC cycle. // // This depends on gcPercent, gcController.heapMarked, and // gcController.heapLive. These must be up to date. // // Callers must call gcControllerState.revise after calling this // function if the GC is enabled. // // mheap_.lock must be held or the world must be stopped. func (c *gcControllerState) commit(isSweepDone bool) { … } // setGCPercent updates gcPercent. commit must be called after. // Returns the old value of gcPercent. // // The world must be stopped, or mheap_.lock must be held. func (c *gcControllerState) setGCPercent(in int32) int32 { … } //go:linkname setGCPercent runtime/debug.setGCPercent func setGCPercent(in int32) (out int32) { … } func readGOGC() int32 { … } // setMemoryLimit updates memoryLimit. commit must be called after // Returns the old value of memoryLimit. // // The world must be stopped, or mheap_.lock must be held. func (c *gcControllerState) setMemoryLimit(in int64) int64 { … } //go:linkname setMemoryLimit runtime/debug.setMemoryLimit func setMemoryLimit(in int64) (out int64) { … } func readGOMEMLIMIT() int64 { … } // addIdleMarkWorker attempts to add a new idle mark worker. // // If this returns true, the caller must become an idle mark worker unless // there's no background mark worker goroutines in the pool. This case is // harmless because there are already background mark workers running. // If this returns false, the caller must NOT become an idle mark worker. // // nosplit because it may be called without a P. // //go:nosplit func (c *gcControllerState) addIdleMarkWorker() bool { … } // needIdleMarkWorker is a hint as to whether another idle mark worker is needed. // // The caller must still call addIdleMarkWorker to become one. This is mainly // useful for a quick check before an expensive operation. // // nosplit because it may be called without a P. // //go:nosplit func (c *gcControllerState) needIdleMarkWorker() bool { … } // removeIdleMarkWorker must be called when a new idle mark worker stops executing. func (c *gcControllerState) removeIdleMarkWorker() { … } // setMaxIdleMarkWorkers sets the maximum number of idle mark workers allowed. // // This method is optimistic in that it does not wait for the number of // idle mark workers to reduce to max before returning; it assumes the workers // will deschedule themselves. func (c *gcControllerState) setMaxIdleMarkWorkers(max int32) { … } // gcControllerCommit is gcController.commit, but passes arguments from live // (non-test) data. It also updates any consumers of the GC pacing, such as // sweep pacing and the background scavenger. // // Calls gcController.commit. // // The heap lock must be held, so this must be executed on the system stack. // //go:systemstack func gcControllerCommit() { … }