const scavengePercent … const retainExtraPercent … const reduceExtraPercent … const maxPagesPerPhysPage … const scavengeCostRatio … const scavChunkHiOccFrac … const scavChunkHiOccPages … // heapRetained returns an estimate of the current heap RSS. func heapRetained() uint64 { … } // gcPaceScavenger updates the scavenger's pacing, particularly // its rate and RSS goal. For this, it requires the current heapGoal, // and the heapGoal for the previous GC cycle. // // The RSS goal is based on the current heap goal with a small overhead // to accommodate non-determinism in the allocator. // // The pacing is based on scavengePageRate, which applies to both regular and // huge pages. See that constant for more information. // // Must be called whenever GC pacing is updated. // // mheap_.lock must be held or the world must be stopped. func gcPaceScavenger(memoryLimit int64, heapGoal, lastHeapGoal uint64) { … } var scavenge … const startingScavSleepRatio … const minScavWorkTime … var scavenger … type scavengerState … // init initializes a scavenger state and wires to the current G. // // Must be called from a regular goroutine that can allocate. func (s *scavengerState) init() { … } // park parks the scavenger goroutine. func (s *scavengerState) park() { … } // ready signals to sysmon that the scavenger should be awoken. func (s *scavengerState) ready() { … } // wake immediately unparks the scavenger if necessary. // // Safe to run without a P. func (s *scavengerState) wake() { … } // sleep puts the scavenger to sleep based on the amount of time that it worked // in nanoseconds. // // Note that this function should only be called by the scavenger. // // The scavenger may be woken up earlier by a pacing change, and it may not go // to sleep at all if there's a pending pacing change. func (s *scavengerState) sleep(worked float64) { … } // controllerFailed indicates that the scavenger's scheduling // controller failed. func (s *scavengerState) controllerFailed() { … } // run is the body of the main scavenging loop. // // Returns the number of bytes released and the estimated time spent // releasing those bytes. // // Must be run on the scavenger goroutine. func (s *scavengerState) run() (released uintptr, worked float64) { … } // Background scavenger. // // The background scavenger maintains the RSS of the application below // the line described by the proportional scavenging statistics in // the mheap struct. func bgscavenge(c chan int) { … } // scavenge scavenges nbytes worth of free pages, starting with the // highest address first. Successive calls continue from where it left // off until the heap is exhausted. force makes all memory available to // scavenge, ignoring huge page heuristics. // // Returns the amount of memory scavenged in bytes. // // scavenge always tries to scavenge nbytes worth of memory, and will // only fail to do so if the heap is exhausted for now. func (p *pageAlloc) scavenge(nbytes uintptr, shouldStop func() bool, force bool) uintptr { … } // printScavTrace prints a scavenge trace line to standard error. // // released should be the amount of memory released since the last time this // was called, and forced indicates whether the scavenge was forced by the // application. // // scavenger.lock must be held. func printScavTrace(releasedBg, releasedEager uintptr, forced bool) { … } // scavengeOne walks over the chunk at chunk index ci and searches for // a contiguous run of pages to scavenge. It will try to scavenge // at most max bytes at once, but may scavenge more to avoid // breaking huge pages. Once it scavenges some memory it returns // how much it scavenged in bytes. // // searchIdx is the page index to start searching from in ci. // // Returns the number of bytes scavenged. // // Must run on the systemstack because it acquires p.mheapLock. // //go:systemstack func (p *pageAlloc) scavengeOne(ci chunkIdx, searchIdx uint, max uintptr) uintptr { … } // fillAligned returns x but with all zeroes in m-aligned // groups of m bits set to 1 if any bit in the group is non-zero. // // For example, fillAligned(0x0100a3, 8) == 0xff00ff. // // Note that if m == 1, this is a no-op. // // m must be a power of 2 <= maxPagesPerPhysPage. func fillAligned(x uint64, m uint) uint64 { … } // findScavengeCandidate returns a start index and a size for this pallocData // segment which represents a contiguous region of free and unscavenged memory. // // searchIdx indicates the page index within this chunk to start the search, but // note that findScavengeCandidate searches backwards through the pallocData. As // a result, it will return the highest scavenge candidate in address order. // // min indicates a hard minimum size and alignment for runs of pages. That is, // findScavengeCandidate will not return a region smaller than min pages in size, // or that is min pages or greater in size but not aligned to min. min must be // a non-zero power of 2 <= maxPagesPerPhysPage. // // max is a hint for how big of a region is desired. If max >= pallocChunkPages, then // findScavengeCandidate effectively returns entire free and unscavenged regions. // If max < pallocChunkPages, it may truncate the returned region such that size is // max. However, findScavengeCandidate may still return a larger region if, for // example, it chooses to preserve huge pages, or if max is not aligned to min (it // will round up). That is, even if max is small, the returned size is not guaranteed // to be equal to max. max is allowed to be less than min, in which case it is as if // max == min. func (m *pallocData) findScavengeCandidate(searchIdx uint, minimum, max uintptr) (uint, uint) { … } type scavengeIndex … // init initializes the scavengeIndex. // // Returns the amount added to sysStat. func (s *scavengeIndex) init(test bool, sysStat *sysMemStat) uintptr { … } // sysGrow updates the index's backing store in response to a heap growth. // // Returns the amount of memory added to sysStat. func (s *scavengeIndex) grow(base, limit uintptr, sysStat *sysMemStat) uintptr { … } // find returns the highest chunk index that may contain pages available to scavenge. // It also returns an offset to start searching in the highest chunk. func (s *scavengeIndex) find(force bool) (chunkIdx, uint) { … } // alloc updates metadata for chunk at index ci with the fact that // an allocation of npages occurred. It also eagerly attempts to collapse // the chunk's memory into hugepage if the chunk has become sufficiently // dense and we're not allocating the whole chunk at once (which suggests // the allocation is part of a bigger one and it's probably not worth // eagerly collapsing). // // alloc may only run concurrently with find. func (s *scavengeIndex) alloc(ci chunkIdx, npages uint) { … } // free updates metadata for chunk at index ci with the fact that // a free of npages occurred. // // free may only run concurrently with find. func (s *scavengeIndex) free(ci chunkIdx, page, npages uint) { … } // nextGen moves the scavenger forward one generation. Must be called // once per GC cycle, but may be called more often to force more memory // to be released. // // nextGen may only run concurrently with find. func (s *scavengeIndex) nextGen() { … } // setEmpty marks that the scavenger has finished looking at ci // for now to prevent the scavenger from getting stuck looking // at the same chunk. // // setEmpty may only run concurrently with find. func (s *scavengeIndex) setEmpty(ci chunkIdx) { … } type atomicScavChunkData … // load loads and unpacks a scavChunkData. func (sc *atomicScavChunkData) load() scavChunkData { … } // store packs and writes a new scavChunkData. store must be serialized // with other calls to store. func (sc *atomicScavChunkData) store(ssc scavChunkData) { … } type scavChunkData … // unpackScavChunkData unpacks a scavChunkData from a uint64. func unpackScavChunkData(sc uint64) scavChunkData { … } // pack returns sc packed into a uint64. func (sc scavChunkData) pack() uint64 { … } const scavChunkHasFree … const scavChunkMaxFlags … const scavChunkFlagsMask … const logScavChunkInUseMax … const scavChunkInUseMask … type scavChunkFlags … // isEmpty returns true if the hasFree flag is unset. func (sc *scavChunkFlags) isEmpty() bool { … } // setEmpty clears the hasFree flag. func (sc *scavChunkFlags) setEmpty() { … } // setNonEmpty sets the hasFree flag. func (sc *scavChunkFlags) setNonEmpty() { … } // shouldScavenge returns true if the corresponding chunk should be interrogated // by the scavenger. func (sc scavChunkData) shouldScavenge(currGen uint32, force bool) bool { … } // alloc updates sc given that npages were allocated in the corresponding chunk. func (sc *scavChunkData) alloc(npages uint, newGen uint32) { … } // free updates sc given that npages was freed in the corresponding chunk. func (sc *scavChunkData) free(npages uint, newGen uint32) { … } type piController … // next provides a new sample to the controller. // // input is the sample, setpoint is the desired point, and period is how much // time (in whatever unit makes the most sense) has passed since the last sample. // // Returns a new value for the variable it's controlling, and whether the operation // completed successfully. One reason this might fail is if error has been growing // in an unbounded manner, to the point of overflow. // // In the specific case of an error overflow occurs, the errOverflow field will be // set and the rest of the controller's internal state will be fully reset. func (c *piController) next(input, setpoint, period float64) (float64, bool) { … } // reset resets the controller state, except for controller error flags. func (c *piController) reset() { … }