%PDF- %PDF-
Direktori : /usr/local/go/src/runtime/testdata/testprog/ |
Current File : //usr/local/go/src/runtime/testdata/testprog/gc.go |
// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "fmt" "os" "runtime" "runtime/debug" "sync/atomic" "time" "unsafe" ) func init() { register("GCFairness", GCFairness) register("GCFairness2", GCFairness2) register("GCSys", GCSys) register("GCPhys", GCPhys) register("DeferLiveness", DeferLiveness) register("GCZombie", GCZombie) } func GCSys() { runtime.GOMAXPROCS(1) memstats := new(runtime.MemStats) runtime.GC() runtime.ReadMemStats(memstats) sys := memstats.Sys runtime.MemProfileRate = 0 // disable profiler itercount := 100000 for i := 0; i < itercount; i++ { workthegc() } // Should only be using a few MB. // We allocated 100 MB or (if not short) 1 GB. runtime.ReadMemStats(memstats) if sys > memstats.Sys { sys = 0 } else { sys = memstats.Sys - sys } if sys > 16<<20 { fmt.Printf("using too much memory: %d bytes\n", sys) return } fmt.Printf("OK\n") } var sink []byte func workthegc() []byte { sink = make([]byte, 1029) return sink } func GCFairness() { runtime.GOMAXPROCS(1) f, err := os.Open("/dev/null") if os.IsNotExist(err) { // This test tests what it is intended to test only if writes are fast. // If there is no /dev/null, we just don't execute the test. fmt.Println("OK") return } if err != nil { fmt.Println(err) os.Exit(1) } for i := 0; i < 2; i++ { go func() { for { f.Write([]byte(".")) } }() } time.Sleep(10 * time.Millisecond) fmt.Println("OK") } func GCFairness2() { // Make sure user code can't exploit the GC's high priority // scheduling to make scheduling of user code unfair. See // issue #15706. runtime.GOMAXPROCS(1) debug.SetGCPercent(1) var count [3]int64 var sink [3]interface{} for i := range count { go func(i int) { for { sink[i] = make([]byte, 1024) atomic.AddInt64(&count[i], 1) } }(i) } // Note: If the unfairness is really bad, it may not even get // past the sleep. // // If the scheduling rules change, this may not be enough time // to let all goroutines run, but for now we cycle through // them rapidly. // // OpenBSD's scheduler makes every usleep() take at least // 20ms, so we need a long time to ensure all goroutines have // run. If they haven't run after 30ms, give it another 1000ms // and check again. time.Sleep(30 * time.Millisecond) var fail bool for i := range count { if atomic.LoadInt64(&count[i]) == 0 { fail = true } } if fail { time.Sleep(1 * time.Second) for i := range count { if atomic.LoadInt64(&count[i]) == 0 { fmt.Printf("goroutine %d did not run\n", i) return } } } fmt.Println("OK") } func GCPhys() { // This test ensures that heap-growth scavenging is working as intended. // // It sets up a specific scenario: it allocates two pairs of objects whose // sizes sum to size. One object in each pair is "small" (though must be // large enough to be considered a large object by the runtime) and one is // large. The small objects are kept while the large objects are freed, // creating two large unscavenged holes in the heap. The heap goal should // also be small as a result (so size must be at least as large as the // minimum heap size). We then allocate one large object, bigger than both // pairs of objects combined. This allocation, because it will tip // HeapSys-HeapReleased well above the heap goal, should trigger heap-growth // scavenging and scavenge most, if not all, of the large holes we created // earlier. const ( // Size must be also large enough to be considered a large // object (not in any size-segregated span). size = 4 << 20 split = 64 << 10 objects = 2 // The page cache could hide 64 8-KiB pages from the scavenger today. maxPageCache = (8 << 10) * 64 // Reduce GOMAXPROCS down to 4 if it's greater. We need to bound the amount // of memory held in the page cache because the scavenger can't reach it. // The page cache will hold at most maxPageCache of memory per-P, so this // bounds the amount of memory hidden from the scavenger to 4*maxPageCache // at most. maxProcs = 4 ) // Set GOGC so that this test operates under consistent assumptions. debug.SetGCPercent(100) procs := runtime.GOMAXPROCS(-1) if procs > maxProcs { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) procs = runtime.GOMAXPROCS(-1) } // Save objects which we want to survive, and condemn objects which we don't. // Note that we condemn objects in this way and release them all at once in // order to avoid having the GC start freeing up these objects while the loop // is still running and filling in the holes we intend to make. saved := make([][]byte, 0, objects+1) condemned := make([][]byte, 0, objects) for i := 0; i < 2*objects; i++ { if i%2 == 0 { saved = append(saved, make([]byte, split)) } else { condemned = append(condemned, make([]byte, size-split)) } } condemned = nil // Clean up the heap. This will free up every other object created above // (i.e. everything in condemned) creating holes in the heap. // Also, if the condemned objects are still being swept, its possible that // the scavenging that happens as a result of the next allocation won't see // the holes at all. We call runtime.GC() twice here so that when we allocate // our large object there's no race with sweeping. runtime.GC() runtime.GC() // Perform one big allocation which should also scavenge any holes. // // The heap goal will rise after this object is allocated, so it's very // important that we try to do all the scavenging in a single allocation // that exceeds the heap goal. Otherwise the rising heap goal could foil our // test. saved = append(saved, make([]byte, objects*size)) // Clean up the heap again just to put it in a known state. runtime.GC() // heapBacked is an estimate of the amount of physical memory used by // this test. HeapSys is an estimate of the size of the mapped virtual // address space (which may or may not be backed by physical pages) // whereas HeapReleased is an estimate of the amount of bytes returned // to the OS. Their difference then roughly corresponds to the amount // of virtual address space that is backed by physical pages. var stats runtime.MemStats runtime.ReadMemStats(&stats) heapBacked := stats.HeapSys - stats.HeapReleased // If heapBacked does not exceed the heap goal by more than retainExtraPercent // then the scavenger is working as expected; the newly-created holes have been // scavenged immediately as part of the allocations which cannot fit in the holes. // // Since the runtime should scavenge the entirety of the remaining holes, // theoretically there should be no more free and unscavenged memory. However due // to other allocations that happen during this test we may still see some physical // memory over-use. overuse := (float64(heapBacked) - float64(stats.HeapAlloc)) / float64(stats.HeapAlloc) // Compute the threshold. // // In theory, this threshold should just be zero, but that's not possible in practice. // Firstly, the runtime's page cache can hide up to maxPageCache of free memory from the // scavenger per P. To account for this, we increase the threshold by the ratio between the // total amount the runtime could hide from the scavenger to the amount of memory we expect // to be able to scavenge here, which is (size-split)*objects. This computation is the crux // GOMAXPROCS above; if GOMAXPROCS is too high the threshold just becomes 100%+ since the // amount of memory being allocated is fixed. Then we add 5% to account for noise, such as // other allocations this test may have performed that we don't explicitly account for The // baseline threshold here is around 11% for GOMAXPROCS=1, capping out at around 30% for // GOMAXPROCS=4. threshold := 0.05 + float64(procs)*maxPageCache/float64((size-split)*objects) if overuse <= threshold { fmt.Println("OK") return } // Physical memory utilization exceeds the threshold, so heap-growth scavenging // did not operate as expected. // // In the context of this test, this indicates a large amount of // fragmentation with physical pages that are otherwise unused but not // returned to the OS. fmt.Printf("exceeded physical memory overuse threshold of %3.2f%%: %3.2f%%\n"+ "(alloc: %d, goal: %d, sys: %d, rel: %d, objs: %d)\n", threshold*100, overuse*100, stats.HeapAlloc, stats.NextGC, stats.HeapSys, stats.HeapReleased, len(saved)) runtime.KeepAlive(saved) } // Test that defer closure is correctly scanned when the stack is scanned. func DeferLiveness() { var x [10]int escape(&x) fn := func() { if x[0] != 42 { panic("FAIL") } } defer fn() x[0] = 42 runtime.GC() runtime.GC() runtime.GC() } //go:noinline func escape(x interface{}) { sink2 = x; sink2 = nil } var sink2 interface{} // Test zombie object detection and reporting. func GCZombie() { // Allocate several objects of unusual size (so free slots are // unlikely to all be re-allocated by the runtime). const size = 190 const count = 8192 / size keep := make([]*byte, 0, (count+1)/2) free := make([]uintptr, 0, (count+1)/2) zombies := make([]*byte, 0, len(free)) for i := 0; i < count; i++ { obj := make([]byte, size) p := &obj[0] if i%2 == 0 { keep = append(keep, p) } else { free = append(free, uintptr(unsafe.Pointer(p))) } } // Free the unreferenced objects. runtime.GC() // Bring the free objects back to life. for _, p := range free { zombies = append(zombies, (*byte)(unsafe.Pointer(p))) } // GC should detect the zombie objects. runtime.GC() println("failed") runtime.KeepAlive(keep) runtime.KeepAlive(zombies) }