mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00: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:
parent
0feebe6eb5
commit
986a31053d
4 changed files with 69 additions and 10 deletions
|
|
@ -15,3 +15,4 @@ func setMaxStack(int) int
|
||||||
func setGCPercent(int32) int32
|
func setGCPercent(int32) int32
|
||||||
func setPanicOnFault(bool) bool
|
func setPanicOnFault(bool) bool
|
||||||
func setMaxThreads(int) int
|
func setMaxThreads(int) int
|
||||||
|
func setMemoryLimit(int64) int64
|
||||||
|
|
|
||||||
|
|
@ -1263,7 +1263,7 @@ func NewGCController(gcPercent int) *GCController {
|
||||||
// space.
|
// space.
|
||||||
g := Escape(new(GCController))
|
g := Escape(new(GCController))
|
||||||
g.gcControllerState.test = true // Mark it as a test copy.
|
g.gcControllerState.test = true // Mark it as a test copy.
|
||||||
g.init(int32(gcPercent))
|
g.init(int32(gcPercent), maxInt64)
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,8 @@ func gcinit() {
|
||||||
|
|
||||||
// Initialize GC pacer state.
|
// Initialize GC pacer state.
|
||||||
// Use the environment variable GOGC for the initial gcPercent value.
|
// 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.startSema = 1
|
||||||
work.markDoneSema = 1
|
work.markDoneSema = 1
|
||||||
|
|
|
||||||
|
|
@ -90,12 +90,21 @@ func init() {
|
||||||
var gcController gcControllerState
|
var gcController gcControllerState
|
||||||
|
|
||||||
type gcControllerState struct {
|
type gcControllerState struct {
|
||||||
|
|
||||||
// Initialized from GOGC. GOGC=off means no GC.
|
// Initialized from GOGC. GOGC=off means no GC.
|
||||||
gcPercent atomic.Int32
|
gcPercent atomic.Int32
|
||||||
|
|
||||||
_ uint32 // padding so following 64-bit values are 8-byte aligned
|
_ 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.
|
// heapMinimum is the minimum heap size at which to trigger GC.
|
||||||
// For small heaps, this overrides the usual GOGC*live set rule.
|
// For small heaps, this overrides the usual GOGC*live set rule.
|
||||||
//
|
//
|
||||||
|
|
@ -352,7 +361,7 @@ type gcControllerState struct {
|
||||||
_ cpu.CacheLinePad
|
_ cpu.CacheLinePad
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *gcControllerState) init(gcPercent int32) {
|
func (c *gcControllerState) init(gcPercent int32, memoryLimit int64) {
|
||||||
c.heapMinimum = defaultHeapMinimum
|
c.heapMinimum = defaultHeapMinimum
|
||||||
|
|
||||||
c.consMarkController = piController{
|
c.consMarkController = piController{
|
||||||
|
|
@ -376,8 +385,9 @@ func (c *gcControllerState) init(gcPercent int32) {
|
||||||
max: 1000,
|
max: 1000,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will also compute and set the GC trigger and goal.
|
|
||||||
c.setGCPercent(gcPercent)
|
c.setGCPercent(gcPercent)
|
||||||
|
c.setMemoryLimit(memoryLimit)
|
||||||
|
c.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// startCycle resets the GC controller's state and computes estimates
|
// startCycle resets the GC controller's state and computes estimates
|
||||||
|
|
@ -1051,11 +1061,9 @@ func (c *gcControllerState) effectiveGrowthRatio() float64 {
|
||||||
return egogc
|
return egogc
|
||||||
}
|
}
|
||||||
|
|
||||||
// setGCPercent updates gcPercent and all related pacer state.
|
// setGCPercent updates gcPercent. commit must be called after.
|
||||||
// Returns the old value of gcPercent.
|
// Returns the old value of gcPercent.
|
||||||
//
|
//
|
||||||
// Calls gcControllerState.commit.
|
|
||||||
//
|
|
||||||
// The world must be stopped, or mheap_.lock must be held.
|
// The world must be stopped, or mheap_.lock must be held.
|
||||||
func (c *gcControllerState) setGCPercent(in int32) int32 {
|
func (c *gcControllerState) setGCPercent(in int32) int32 {
|
||||||
if !c.test {
|
if !c.test {
|
||||||
|
|
@ -1068,8 +1076,6 @@ func (c *gcControllerState) setGCPercent(in int32) int32 {
|
||||||
}
|
}
|
||||||
c.heapMinimum = defaultHeapMinimum * uint64(in) / 100
|
c.heapMinimum = defaultHeapMinimum * uint64(in) / 100
|
||||||
c.gcPercent.Store(in)
|
c.gcPercent.Store(in)
|
||||||
// Update pacing in response to gcPercent change.
|
|
||||||
c.commit()
|
|
||||||
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
@ -1080,6 +1086,7 @@ func setGCPercent(in int32) (out int32) {
|
||||||
systemstack(func() {
|
systemstack(func() {
|
||||||
lock(&mheap_.lock)
|
lock(&mheap_.lock)
|
||||||
out = gcController.setGCPercent(in)
|
out = gcController.setGCPercent(in)
|
||||||
|
gcController.commit()
|
||||||
gcPaceSweeper(gcController.trigger)
|
gcPaceSweeper(gcController.trigger)
|
||||||
gcPaceScavenger(gcController.heapGoal, gcController.lastHeapGoal)
|
gcPaceScavenger(gcController.heapGoal, gcController.lastHeapGoal)
|
||||||
unlock(&mheap_.lock)
|
unlock(&mheap_.lock)
|
||||||
|
|
@ -1105,6 +1112,56 @@ func readGOGC() int32 {
|
||||||
return 100
|
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 {
|
type piController struct {
|
||||||
kp float64 // Proportional constant.
|
kp float64 // Proportional constant.
|
||||||
ti float64 // Integral time constant.
|
ti float64 // Integral time constant.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue