pkg reflect, func Swapper(interface{}) func(int, int)
pkg runtime, func MutexProfile([]BlockProfileRecord) (int, bool)
pkg runtime, func SetMutexProfileFraction(int) int
+pkg runtime, type MemStats struct, NumForcedGC uint32
pkg sort, func Slice(interface{}, func(int, int) bool)
pkg sort, func SliceIsSorted(interface{}, func(int, int) bool) bool
pkg sort, func SliceStable(interface{}, func(int, int) bool)
)
func TestMemStats(t *testing.T) {
+ // Make sure there's at least one forced GC.
+ GC()
+
// Test that MemStats has sane values.
st := new(MemStats)
ReadMemStats(st)
st.HeapInuse == 0 || st.HeapObjects == 0 || st.StackInuse == 0 ||
st.StackSys == 0 || st.MSpanInuse == 0 || st.MSpanSys == 0 || st.MCacheInuse == 0 ||
st.MCacheSys == 0 || st.BuckHashSys == 0 || st.GCSys == 0 || st.OtherSys == 0 ||
- st.NextGC == 0 {
+ st.NextGC == 0 || st.NumForcedGC == 0 {
t.Fatalf("Zero value: %+v", *st)
}
st.HeapIdle > 1e10 || st.HeapInuse > 1e10 || st.HeapObjects > 1e10 || st.StackInuse > 1e10 ||
st.StackSys > 1e10 || st.MSpanInuse > 1e10 || st.MSpanSys > 1e10 || st.MCacheInuse > 1e10 ||
st.MCacheSys > 1e10 || st.BuckHashSys > 1e10 || st.GCSys > 1e10 || st.OtherSys > 1e10 ||
- st.NextGC > 1e10 || st.NumGC > 1e9 || st.PauseTotalNs > 1e11 {
+ st.NextGC > 1e10 || st.NumGC > 1e9 || st.NumForcedGC > 1e9 || st.PauseTotalNs > 1e11 {
t.Fatalf("Insanely high value (overflow?): %+v", *st)
}
t.Fatalf("PauseTotalNs(%d) < sum PauseNs(%d)", st.PauseTotalNs, pauseTotal)
}
}
+
+ if st.NumForcedGC > st.NumGC {
+ t.Fatalf("NumForcedGC(%d) > NumGC(%d)", st.NumForcedGC, st.NumGC)
+ }
}
func TestStringConcatenationAllocs(t *testing.T) {
const (
gcBackgroundMode gcMode = iota // concurrent GC and sweep
gcForceMode // stop-the-world GC now, concurrent sweep
- gcForceBlockMode // stop-the-world GC now and STW sweep
+ gcForceBlockMode // stop-the-world GC now and STW sweep (forced by user)
)
// gcShouldStart returns true if the exit condition for the _GCoff
}
}
+ // For stats, check if this GC was forced by the user.
+ forced := mode != gcBackgroundMode
+
// In gcstoptheworld debug mode, upgrade the mode accordingly.
// We do this after re-checking the transition condition so
// that multiple goroutines that detect the heap trigger don't
work.tMark, work.tMarkTerm = t, t
work.heapGoal = work.heap0
+ if forced {
+ memstats.numforcedgc++
+ }
+
// Perform mark termination. This will restart the world.
gcMarkTermination()
}
pause_ns [256]uint64 // circular buffer of recent gc pause lengths
pause_end [256]uint64 // circular buffer of recent gc end times (nanoseconds since 1970)
numgc uint32
+ numforcedgc uint32 // number of user-forced GCs
gc_cpu_fraction float64 // fraction of CPU time used by GC
enablegc bool
debuggc bool
// must be complete.
gc_trigger uint64
- _ uint32 // force 8-byte alignment of heap_live and prevent an alignment check crash on MIPS32.
-
// heap_live is the number of bytes considered live by the GC.
// That is: retained by the most recent GC plus allocated
// since then. heap_live <= heap_alloc, since heap_alloc
// NumGC is the number of completed GC cycles.
NumGC uint32
+ // NumForcedGC is the number of GC cycles that were forced by
+ // the application calling the GC function.
+ NumForcedGC uint32
+
// GCCPUFraction is the fraction of this program's available
// CPU time used by the GC since the program started.
//