Go

Explain escape analysis. Which of these allocate on the heap?

Question 389HardGo 1.22 to 1.25
type Point struct{ X, Y int }

func a() Point   { p := Point{1, 2}; return p }  // (1)
func b() *Point  { p := Point{1, 2}; return &p } // (2)
func c() int     { p := &Point{1, 2}; return p.X } // (3)
func d(n int) []int { return make([]int, n) }  // (4)
func e() []int   { return make([]int, 8) }     // (5) returned
func f()         { s := make([]int, 8); _ = s[0] } // (6)
func g(p Point)  { fmt.Println(p) }            // (7)

Answers (confirm with go build -gcflags=-m):

  1. Stack — returned by value (copied).
  2. Heap — "moved to heap: p"; the pointer outlives the frame.
  3. Stack — the pointer never leaves c; & alone doesn't force heap allocation.
  4. Heap — the slice escapes via return. (Non-escaping make with a non-constant size also went to the heap before Go 1.25; since 1.25 the compiler can use a small 32-byte stack buffer when the requested size fits, falling back to the heap otherwise.)
  5. Heap — escapes via return, even though the size is constant.
  6. Stack — constant, small (< 64 KiB), doesn't escape.
  7. Heap — p is converted to any for a variadic ...any parameter, and fmt's arguments escape.

Other common escape causes: storing a pointer in a global, a heap object, or a channel; closures capturing variables that outlive the function; calling methods via interfaces (the compiler can't see the callee); slices whose backing array grows via append beyond a stack-known size. Why it matters: each heap allocation costs malloc time plus future GC work. Interviewer is looking for: "the compiler decides, not new vs &", and the habit of verifying with -m and allocs/op instead of guessing.

More on Performance, Profiling & Testing

All 38 Performance, Profiling & Testing questions