]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: add number of forced GCs to MemStats
authorAustin Clements <austin@google.com>
Tue, 6 Dec 2016 22:42:42 +0000 (17:42 -0500)
committerAustin Clements <austin@google.com>
Wed, 7 Dec 2016 20:59:16 +0000 (20:59 +0000)
This adds a counter for the number of times the application forced a
GC by, e.g., calling runtime.GC(). This is useful for detecting
applications that are overusing/abusing runtime.GC() or
debug.FreeOSMemory().

Fixes #18217.

Change-Id: I990ab7a313c1b3b7a50a3d44535c460d7c54f47d
Reviewed-on: https://go-review.googlesource.com/34067
Reviewed-by: Russ Cox <rsc@golang.org>
api/go1.8.txt
src/runtime/malloc_test.go
src/runtime/mgc.go
src/runtime/mstats.go

index d93de98e1a2b459c15c32bf0ff99bbbbbe86952c..6ca0f3638c35e392f6922b8a798e4c2542fd91a8 100644 (file)
@@ -238,6 +238,7 @@ pkg plugin, type Symbol interface {}
 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)
index 767b51f453bb124eb12b7ed5e474c1c3cdf7bc9d..0cf9cfbf4279ca4974573b5ee2e26ca63ef8b112 100644 (file)
@@ -13,6 +13,9 @@ import (
 )
 
 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)
@@ -24,7 +27,7 @@ func TestMemStats(t *testing.T) {
                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)
        }
 
@@ -33,7 +36,7 @@ func TestMemStats(t *testing.T) {
                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)
        }
 
@@ -72,6 +75,10 @@ func TestMemStats(t *testing.T) {
                        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) {
index cc79d4cfff377b764b7d863806f0d1670cf7f241..0f0b0962e95c34465089133a133f6e7201a4a3f5 100644 (file)
@@ -902,7 +902,7 @@ type gcMode int
 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
@@ -966,6 +966,9 @@ func gcStart(mode gcMode, forceTrigger bool) {
                }
        }
 
+       // 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
@@ -1070,6 +1073,10 @@ func gcStart(mode gcMode, forceTrigger bool) {
                work.tMark, work.tMarkTerm = t, t
                work.heapGoal = work.heap0
 
+               if forced {
+                       memstats.numforcedgc++
+               }
+
                // Perform mark termination. This will restart the world.
                gcMarkTermination()
        }
index b80ab11389f4914fc572d32e7f1ece6bbe3941ad..4e111a14fe96133e4b6b292bcb12eb11d208024e 100644 (file)
@@ -77,6 +77,7 @@ type mstats struct {
        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
@@ -100,8 +101,6 @@ type mstats struct {
        // 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
@@ -365,6 +364,10 @@ type MemStats struct {
        // 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.
        //