Runtime Basics
10 examples to get you started with Runtime & GC - 7 basic and 3 intermediate.
Search across all documentation pages
10 examples to get you started with Runtime & GC - 7 basic and 3 intermediate.
mkdir rtdemo && cd rtdemo && go mod init example.com/rtdemo.main.go (or separate files in one package) and run with go run ..GODEBUG=gctrace=1 go run . (verbose; use on small programs).runtime.ReadMemStats snapshots allocator and GC counters.
package main
import (
"fmt"
"runtime"
)
func main() {
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
fmt.Printf("heap alloc: %d KiB\n", ms.HeapAlloc/1024)
fmt.Printf("goroutines: %d\n", runtime.NumGoroutine())
}HeapAlloc is bytes currently allocated on the heap (reachable objects).ReadMemStats stops the world briefly - do not call every request in hot paths.go_memstats_* exporters in production instead of polling in handlers.Related: The Go Runtime: Scheduler, GC, and Memory - runtime mental model
Count live goroutines for leak triage.
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
fmt.Println("start:", runtime.NumGoroutine())
go func() {
time.Sleep(200 * time.Millisecond)
}()
time.Sleep(10 * time.Millisecond)
fmt.Println("with worker:", runtime.NumGoroutine())
}WaitGroup waits or blocked channel sends.Related: Goroutine Stacks & Stack Growth - per-goroutine stack cost
See how many Ps the scheduler uses.
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println("NumCPU:", runtime.NumCPU())
fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))
}GOMAXPROCS(0) reads the current setting without changing it.Related: Goroutines: Creation Cost & Scheduling Overview - GPM model
Force a collection to measure live heap after cleanup.
package main
import (
"fmt"
"runtime"
)
func main() {
data := make([]byte, 10<<20) // 10 MiB
_ = data
runtime.GC()
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
fmt.Printf("after GC heap: %d MiB\n", ms.HeapAlloc/(1<<20))
}runtime.GC() runs a full cycle and returns when complete.HeapAlloc high even immediately after GC.Related: Garbage Collector: Tri-Color Mark-Sweep & Pacing - what a cycle does
Inspect cumulative STW pause time.
package main
import (
"fmt"
"runtime"
)
func main() {
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
fmt.Printf("GC cycles: %d\n", ms.NumGC)
fmt.Printf("pause total: %d ms\n", ms.PauseTotalNs/1e6)
if ms.NumGC > 0 {
last := ms.PauseNs[(ms.NumGC+255)%256]
fmt.Printf("last pause: %d µs\n", last/1e3)
}
}PauseNs is a circular buffer of recent pause lengths.GOGC or GOMEMLIMIT changes under load.Related: GOGC, GOMEMLIMIT & Finalizers - tuning knobs
See how many objects each cycle allocates.
package main
import (
"fmt"
"runtime"
)
func allocN(n int) {
for i := 0; i < n; i++ {
_ = make([]byte, 1024)
}
}
func main() {
var before runtime.MemStats
runtime.ReadMemStats(&before)
allocN(1000)
var after runtime.MemStats
runtime.ReadMemStats(&after)
fmt.Println("mallocs delta:", after.Mallocs-before.Mallocs)
}Mallocs and Frees counters increase monotonically for the process lifetime.go test -bench with benchmem reports allocations per operation.Related: Escape Analysis & Stack vs Heap Allocation - reducing heap allocs
Return unused spans to the OS after a spike.
package main
import (
"fmt"
"runtime"
"runtime/debug"
)
func main() {
_ = make([]byte, 50<<20)
runtime.GC()
debug.FreeOSMemory()
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
fmt.Printf("heap idle: %d MiB\n", ms.HeapIdle/(1<<20))
}FreeOSMemory encourages returning them to the OS.Related: Memory Leaks in Go: Common Causes - leak patterns
Print one line per GC cycle to stderr.
package main
func main() {
for i := 0; i < 1_000_000; i++ {
_ = make([]byte, 256)
}
}GODEBUG=gctrace=1 go run .GOGC.Related: Runtime & GC Best Practices - production tuning workflow
Read structured GC and memory metrics (Go 1.16+).
package main
import (
"fmt"
"runtime/metrics"
)
func main() {
samples := []metrics.Sample{
{Name: "/gc/heap/live:bytes"},
{Name: "/sched/goroutines:goroutines"},
}
metrics.Read(samples)
fmt.Println("live heap:", samples[0].Value.Uint64())
fmt.Println("goroutines:", samples[1].Value.Uint64())
}MemStats in exporters.go doc runtime/metrics for the full catalog./gc/cycles/total:gc-cycles pairs well with CPU profiles during GC regressions.Related: CPU & Heap Profiling with pprof - profile under load
Correlate StackInuse with goroutine fan-out.
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10_000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
var buf [4096]byte // grows stack if needed
_ = buf
}()
}
wg.Wait()
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
fmt.Printf("stack inuse: %d MiB, goroutines: %d\n",
ms.StackInuse/(1<<20), runtime.NumGoroutine())
}StackInuse is aggregate stack memory across goroutines.StackInuse tracks request count linearly.Related: Goroutine Stacks & Stack Growth - growth and copy rules
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 18, 2026