Under The Hood
backend go Go 1.21+ (compiler escape analysis)

Escape Analysis: Pointer Escapes, Interface Wrapping, Stack Growth

Last updated
Prerequisites:
Go pointers and stack/heap basics
Understanding of compiler optimizations
  • go
  • escape-analysis
  • stack
  • heap
  • allocation
  • compiler

Read at your depth

The practical view

Escape analysis is a compile-time optimization that decides whether a value can live on the goroutine's stack instead of the heap. If a value doesn't escape the function (no pointer returned, not stored in a global, not passed to an interface that leaks it), the compiler allocates it on the stack — zero GC pressure and fast allocation. If it escapes, it's heap-allocated. You can see the decision with go build -gcflags='-m' (allocs: x escapes to heap). This is why small local values are cheap; interfaces and pointers to returned values often trigger escapes.

The same idea in other frameworks

react equivalent
N/A — Go compiler concern.

Legacy vs modern

Value that escapes (via interface) vs value that stays on the stack

Passing a value to an interface forces a heap allocation; keeping it concrete lets escape analysis stack-allocate it.

before → after
Interface wrapper forces escape
var v any = 42 // ❌ OLD: interface conversion — escapes to heap
Concrete value stays on the stack
func sum(n int) int { // ✅ NEW: local ints stay on the stack
  var total int
  for i := 0; i < n; i++ { total += i }
  return total
}

Interview gotchas

Context

This tests the escape analysis triggers — interviewers want the concrete conditions (returned pointers, interfaces, globals, closures) and the tooling to observe them.

The mechanical answer

A value escapes when the compiler cannot prove it stays within the current stack frame. Common triggers: returning a pointer to it (or a struct containing one), storing it in a global, storing it in a heap-allocated structure (map/slice that outlives the call), passing it to a function that retains the reference (e.g. storing the argument in a field or a closure), and interface conversion — an interface value carries a pointer to the underlying data, so boxing a value into an interface (including any, fmt.Println, etc.) makes it escape. Closures escape when the closure itself escapes. To find escapes, use go build -gcflags='-m' (and -m -m for more detail) and look for 'escapes to heap'. The fix is usually to avoid interfaces/pointers in hot paths or restructure so the value stays local.

Trap

The naive answer is 'pointers always escape'. A pointer used only within the function (or within inlined boundaries) can stay on the stack; returning it forces escape. Another trap: assuming small values in interfaces are cheap — any interface boxing of a value escapes and allocates. Candidates who enumerate the triggers and the -m tooling show real understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.