Go

How do you inspect escape analysis decisions, and how do you read the output?

Question 332MediumGo 1.22 to 1.25

Build with go build -gcflags='-m' ./.... Use -m -m for the reason chain behind each decision, and -gcflags='-m -l' to turn off inlining so the output is easier to follow.

package main

import "fmt"

type Point struct{ X, Y int }

func NewPoint(x, y int) *Point { return &Point{x, y} }

func Sum(xs []int) (s int) {
    for _, v := range xs {
        s += v
    }
    return
}

func main() {
    p := NewPoint(1, 2)
    fmt.Println(p.X, Sum([]int{1, 2, 3}))
}

Output (Go 1.25, gofmt'd file with tab indentation):

./main.go:7:6: can inline NewPoint
./main.go:9:6: can inline Sum
./main.go:17:15: inlining call to NewPoint
./main.go:18:22: inlining call to Sum
./main.go:18:13: inlining call to fmt.Println
./main.go:7:41: &Point{...} escapes to heap
./main.go:9:10: xs does not escape
./main.go:17:15: &Point{...} does not escape
./main.go:18:13: ... argument does not escape
./main.go:18:15: p.X escapes to heap
./main.go:18:22: s escapes to heap
./main.go:18:28: []int{...} does not escape

Notice that &Point{} escapes inside NewPoint but not in main. Once NewPoint is inlined, the compiler can see the pointer never leaves main. Inlining is what makes small constructors cheap. "moved to heap: x" means a named variable was heap-allocated. "leaking param: p" means the parameter flows out of the function, for example into a return value or a global. "p.X escapes to heap" and "s escapes to heap" refer to the int values being boxed into any for fmt.Println. Values 0-255 use static data, so no allocation actually happens at run time here.

More on Memory, GC & Runtime Internals

All 38 Memory, GC & Runtime Internals questions