Escape Analysis
Go · Advanced · Run with go run
Explanation
Escape analysis is a compile-time pass that decides whether a value lives on the stack (cheap, freed automatically when the function returns) or escapes to the heap (managed by the garbage collector). A value escapes when its lifetime outlives the function — typically when its address is returned, stored in a longer-lived structure, or captured by a closure that outlives the call. `go build -gcflags='-m'` prints each decision.
Real-World Scenario
A hot function allocates a small struct per call. Profiling shows GC pressure. Running with `-gcflags='-m'` reveals the struct "escapes to heap" because its pointer is returned. Refactoring to return the struct by value (not by pointer) keeps it on the stack and eliminates the allocation.
Quick Snippet
// Inspect with: go build -gcflags='-m' escape_analysis.go
// Does NOT escape — value stays on the stack
func sumLocal() int {
x := [3]int{1, 2, 3} // "does not escape"
return x[0] + x[1] + x[2]
}
// ESCAPES — the pointer outlives the function, so n moves to the heap
func newCounter() *int {
n := 0 // "moved to heap: n"
return &n
}
// ESCAPES — captured by a returned closure
func adder() func(int) int {
sum := 0 // "moved to heap: sum"
return func(x int) int { sum += x; return sum }
}Full Example
// Compile: go run escape_analysis.go
// Inspect decisions: go build -gcflags='-m' escape_analysis.go
// Output:
// sumLocal = 6
// counter = 7
// adder: 1 3 6
//
// sumLocal's array stays on the stack. newCounter's int and adder's sum
// escape to the heap because their lifetimes outlive the function.
package main
import "fmt"
// Does not escape — the array lives on the stack.
func sumLocal() int {
x := [3]int{1, 2, 3}
return x[0] + x[1] + x[2]
}
// Escapes — &n is returned, so n moves to the heap.
func newCounter() *int {
n := 0
return &n
}
// Escapes — sum is captured by a closure that outlives adder.
func adder() func(int) int {
sum := 0
return func(x int) int { sum += x; return sum }
}
func main() {
fmt.Println("sumLocal =", sumLocal())
c := newCounter()
*c = 7
fmt.Println("counter =", *c)
add := adder()
fmt.Println("adder:", add(1), add(2), add(3))
}
go run escape_analysis.go