liveBook · Manning

Execution context stack

As explained in 100 Go Mistakes and How to Avoid Them

A call stack is the runtime-ordered sequence of active function calls through which control returns during execution.

A common mistake is to assume that returning a pointer to a local variable leaves it pointing into a finished call. In Go, each goroutine has its own growable, last-in-first-out stack. Entering a function creates a stack frame for that call; returning makes its stack-resident locals inaccessible, even if their bytes have not yet been overwritten. section 12.5.1

func main() {
    a := 3
    b := 2
 
    c := sumValue(a, b)        #1
    println(c)                 #2
}
 
//go:noinline                  #3
func sumValue(x, y int) int {
    z := x + y
    return z
}

Trace the calls: main has a frame for a and b; calling sumValue adds a frame for x, y, and z. After sumValue returns, execution continues in main and its frame remains valid. Inlining is disabled so the call and its frame can be observed. section 12.5.1

A frame's end does not make a returned pointer invalid. If the compiler cannot prove that a local variable is no longer referenced after return, escape analysis allocates it on the heap instead. Passing pointers to variables in a caller's still-active frame down to a called function does not, by itself, require those variables to escape. section 12.5.1 section 12.5.2

func main() {
    a := 3
    b := 2
 
    c := sumPtr(a, b)
    println(*c)
}
 
//go:noinline
func sumPtr(x, y int) *int {     #1
    z := x + y
    return &z
}

Unlike sumValue, sumPtr returns &z. Because z must remain valid after sumPtr returns, it cannot remain solely in that call's stack frame. section 12.5.1

This distinction matters when optimizing allocations: stack allocations are inexpensive, while heap allocations require garbage-collector work. Do not infer allocation solely from the shape of a call or pointer; inspect compiler escape decisions with go build -gcflags "-m=2" when the distinction matters. section 12.11 section 12.5.2

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Traces frame creation and return in a worked call, then explains when escape analysis places variables on the heap.
Examples
Traces main calling sumValue, contrasts a returned pointer in sumPtr, and shows benchmark and compiler escape output.
What it assumes
Assumes familiarity with Go functions and pointers while defining stack frames and explaining escape analysis through examples.
What you can do after
Diagnose/debug with it by tracing frame lifetimes and checking suspected heap escapes with go build -gcflags "-m=2".
How it connects
Connects goroutines and stack frames to pointers, escape analysis, heap allocations, and garbage collection.

Also explained in

Compare how 8 books explain execution context stack →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Go Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.