1
0
mirror of https://github.com/golang/go synced 2024-11-24 07:00:13 -07:00

runtime: add a non-functional memory limit to the pacer

Nothing much to see here, just some plumbing to make latter CLs smaller
and clearer.

For #48409.

Change-Id: Ide23812d5553e0b6eea5616c277d1a760afb4ed0
Reviewed-on: https://go-review.googlesource.com/c/go/+/393401
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
Michael Anthony Knyszek 2022-03-02 20:49:36 +00:00 committed by Michael Knyszek
parent 0feebe6eb5
commit 986a31053d
4 changed files with 69 additions and 10 deletions

View File

@ -15,3 +15,4 @@ func setMaxStack(int) int
func setGCPercent(int32) int32
func setPanicOnFault(bool) bool
func setMaxThreads(int) int
func setMemoryLimit(int64) int64

View File

@ -1263,7 +1263,7 @@ func NewGCController(gcPercent int) *GCController {
// space.
g := Escape(new(GCController))
g.gcControllerState.test = true // Mark it as a test copy.
g.init(int32(gcPercent))
g.init(int32(gcPercent), maxInt64)
return g
}

View File

@ -158,7 +158,8 @@ func gcinit() {
// Initialize GC pacer state.
// Use the environment variable GOGC for the initial gcPercent value.
gcController.init(readGOGC())
// Use the environment variable GOMEMLIMIT for the initial memoryLimit value.
gcController.init(readGOGC(), maxInt64)
work.startSema = 1
work.markDoneSema = 1

View File

@ -90,12 +90,21 @@ func init() {
var gcController gcControllerState
type gcControllerState struct {
// Initialized from GOGC. GOGC=off means no GC.
gcPercent atomic.Int32
_ uint32 // padding so following 64-bit values are 8-byte aligned
// memoryLimit is the soft memory limit in bytes.
//
// Initialized from GOMEMLIMIT. GOMEMLIMIT=off is equivalent to MaxInt64
// which means no soft memory limit in practice.
//
// This is an int64 instead of a uint64 to more easily maintain parity with
// the SetMemoryLimit API, which sets a maximum at MaxInt64. This value
// should never be negative.
memoryLimit atomic.Int64
// heapMinimum is the minimum heap size at which to trigger GC.
// For small heaps, this overrides the usual GOGC*live set rule.
//
@ -352,7 +361,7 @@ type gcControllerState struct {
_ cpu.CacheLinePad
}
func (c *gcControllerState) init(gcPercent int32) {
func (c *gcControllerState) init(gcPercent int32, memoryLimit int64) {
c.heapMinimum = defaultHeapMinimum
c.consMarkController = piController{
@ -376,8 +385,9 @@ func (c *gcControllerState) init(gcPercent int32) {
max: 1000,
}
// This will also compute and set the GC trigger and goal.
c.setGCPercent(gcPercent)
c.setMemoryLimit(memoryLimit)
c.commit()
}
// startCycle resets the GC controller's state and computes estimates
@ -1051,11 +1061,9 @@ func (c *gcControllerState) effectiveGrowthRatio() float64 {
return egogc
}
// setGCPercent updates gcPercent and all related pacer state.
// setGCPercent updates gcPercent. commit must be called after.
// Returns the old value of gcPercent.
//
// Calls gcControllerState.commit.
//
// The world must be stopped, or mheap_.lock must be held.
func (c *gcControllerState) setGCPercent(in int32) int32 {
if !c.test {
@ -1068,8 +1076,6 @@ func (c *gcControllerState) setGCPercent(in int32) int32 {
}
c.heapMinimum = defaultHeapMinimum * uint64(in) / 100
c.gcPercent.Store(in)
// Update pacing in response to gcPercent change.
c.commit()
return out
}
@ -1080,6 +1086,7 @@ func setGCPercent(in int32) (out int32) {
systemstack(func() {
lock(&mheap_.lock)
out = gcController.setGCPercent(in)
gcController.commit()
gcPaceSweeper(gcController.trigger)
gcPaceScavenger(gcController.heapGoal, gcController.lastHeapGoal)
unlock(&mheap_.lock)
@ -1105,6 +1112,56 @@ func readGOGC() int32 {
return 100
}
// 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 {
if !c.test {
assertWorldStoppedOrLockHeld(&mheap_.lock)
}
out := c.memoryLimit.Load()
if in >= 0 {
c.memoryLimit.Store(in)
}
return out
}
//go:linkname setMemoryLimit runtime/debug.setMemoryLimit
func setMemoryLimit(in int64) (out int64) {
// Run on the system stack since we grab the heap lock.
systemstack(func() {
lock(&mheap_.lock)
out = gcController.setMemoryLimit(in)
if in < 0 || out == in {
// If we're just checking the value or not changing
// it, there's no point in doing the rest.
unlock(&mheap_.lock)
return
}
gcController.commit()
gcPaceSweeper(gcController.trigger)
gcPaceScavenger(gcController.heapGoal, gcController.lastHeapGoal)
unlock(&mheap_.lock)
})
return out
}
func readGOMEMLIMIT() int64 {
p := gogetenv("GOMEMLIMIT")
if p == "" || p == "off" {
return maxInt64
}
n, ok := parseByteCount(p)
if !ok {
print("GOMEMLIMIT=", p, "\n")
throw("malformed GOMEMLIMIT; see `go doc runtime/debug.SetMemoryLimit`")
}
return n
}
type piController struct {
kp float64 // Proportional constant.
ti float64 // Integral time constant.