1
0
mirror of https://github.com/golang/go synced 2024-09-30 00:34:31 -06:00

runtime: test that periodic GC works

We've broken periodic GC a few times without noticing because there's
no test for it, partly because you have to wait two minutes to see if
it happens. This exposes control of the periodic GC timeout to runtime
tests and adds a test that cranks it down to zero and sleeps for a bit
to make sure periodic GCs happen.

Change-Id: I3ec44e967e99f4eda752f85c329eebd18b87709e
Reviewed-on: https://go-review.googlesource.com/13169
Reviewed-by: Rick Hudson <rlh@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
This commit is contained in:
Austin Clements 2015-08-05 11:35:28 -04:00
parent 829cc349c5
commit e01be84149
3 changed files with 46 additions and 9 deletions

View File

@ -155,3 +155,5 @@ const PtrSize = ptrSize
var TestingAssertE2I2GC = &testingAssertE2I2GC
var TestingAssertE2T2GC = &testingAssertE2T2GC
var ForceGCPeriod = &forcegcperiod

View File

@ -199,6 +199,37 @@ func TestHugeGCInfo(t *testing.T) {
}
}
func TestPeriodicGC(t *testing.T) {
// Make sure we're not in the middle of a GC.
runtime.GC()
var ms1, ms2 runtime.MemStats
runtime.ReadMemStats(&ms1)
// Make periodic GC run continuously.
orig := *runtime.ForceGCPeriod
*runtime.ForceGCPeriod = 0
// Let some periodic GCs happen. In a heavily loaded system,
// it's possible these will be delayed, so this is designed to
// succeed quickly if things are working, but to give it some
// slack if things are slow.
var numGCs uint32
const want = 2
for i := 0; i < 20 && numGCs < want; i++ {
time.Sleep(5 * time.Millisecond)
// Test that periodic GC actually happened.
runtime.ReadMemStats(&ms2)
numGCs = ms2.NumGC - ms1.NumGC
}
*runtime.ForceGCPeriod = orig
if numGCs < want {
t.Fatalf("no periodic GC: got %v GCs, want >= 2", numGCs)
}
}
func BenchmarkSetTypePtr(b *testing.B) {
benchSetType(b, new(*byte))
}

View File

@ -2962,10 +2962,14 @@ func checkdead() {
throw("all goroutines are asleep - deadlock!")
}
func sysmon() {
// If we go two minutes without a garbage collection, force one to run.
forcegcperiod := int64(2 * 60 * 1e9)
// forcegcperiod is the maximum time in nanoseconds between garbage
// collections. If we go this long without a garbage collection, one
// is forced to run.
//
// This is a variable for testing purposes. It normally doesn't change.
var forcegcperiod int64 = 2 * 60 * 1e9
func sysmon() {
// If a heap span goes unused for 5 minutes after a garbage collection,
// we hand it back to the operating system.
scavengelimit := int64(5 * 60 * 1e9)
@ -2979,12 +2983,6 @@ func sysmon() {
lastscavenge := nanotime()
nscavenge := 0
// Make wake-up period small enough for the sampling to be correct.
maxsleep := forcegcperiod / 2
if scavengelimit < forcegcperiod {
maxsleep = scavengelimit / 2
}
lasttrace := int64(0)
idle := 0 // how many cycles in succession we had not wokeup somebody
delay := uint32(0)
@ -3003,6 +3001,12 @@ func sysmon() {
if atomicload(&sched.gcwaiting) != 0 || atomicload(&sched.npidle) == uint32(gomaxprocs) {
atomicstore(&sched.sysmonwait, 1)
unlock(&sched.lock)
// Make wake-up period small enough
// for the sampling to be correct.
maxsleep := forcegcperiod / 2
if scavengelimit < forcegcperiod {
maxsleep = scavengelimit / 2
}
notetsleep(&sched.sysmonnote, maxsleep)
lock(&sched.lock)
atomicstore(&sched.sysmonwait, 0)