liveBook · Manning

Heap

As explained in 100 Go Mistakes and How to Avoid Them

A memory heap is a region of runtime memory shared by goroutines for objects whose lifetimes cannot be confined to a stack frame.

A pointer does not automatically mean a heap allocation. In Go, the compiler chooses between a goroutine’s stack and the heap according to whether a value can safely remain in a stack frame. Each goroutine has its own stack; goroutines share the heap. Heap allocation is slower than stack allocation and leaves memory for the garbage collector (GC) to reclaim. section 12.5.1 section 12.5.2 section 12.11

A function call creates a stack frame. After the function returns, its former local addresses cannot safely serve a caller’s references: the stack space can be reused. If the compiler cannot prove that a local value will stop being referenced at return, it allocates that value on the heap instead. 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
}

In this pointer-returning example, the caller receives the address of z. Returning that address does not leave a dangling pointer: z escapes its function frame and is allocated on the heap. section 12.5.1 section 12.5.2

Measure the cost of returning an address

The supplied benchmarks contrast returning a sum as an int with returning a pointer to the sum. The value-returning version reports 0 B/op and 0 allocs/op; the pointer-returning version reports 8 B/op and 1 alloc/op. Their reported times are 1.261 ns/op and 14.84 ns/op, respectively. These results illustrate this example, not a rule that every pointer is expensive. section 12.5.1

Direction matters. Passing pointers to a called function can leave the pointed-to values on the caller’s stack: those addresses remain valid during the call. Returning a pointer to a local value is different because the caller may use it after the callee returns. Thus, avoid treating “uses a pointer” as a diagnosis; inspect where the value can be referenced. section 12.5.2

Other situations that may put a value on the heap include:

  • A global variable, a pointer sent through a channel, or a value sent through a channel that contains a reference to a local variable. section 12.5.2
  • A local value too large for the stack, or one whose size is unknown to the compiler, such as a slice made with a variable length. section 12.5.2
  • A slice backing array reallocated by append. section 12.5.2
$ go build -gcflags "-m=2"
...
./main.go:12:2: z escapes to heap:

Check a suspected escape with go build -gcflags "-m=2". The compiler output in this example reports that z escapes to the heap. The listed escape patterns are not exhaustive and can change across Go versions, so use the diagnostic rather than relying on a blanket rule. section 12.5.2

Heap use also affects GC work. Go’s concurrent mark-and-sweep collector identifies heap objects still in use and reclaims unreferenced memory. GOGC sets a heap-growth percentage for triggering another cycle: at its stated default of 100%, a 128 MB heap after one GC would reach 256 MB before the next growth-triggered cycle. After an allocation peak, a periodic scavenger can return unneeded memory to the operating system. Reduce allocations where measurement shows a worthwhile benefit, rather than making clear code more complex without evidence of a bottleneck. section 12.9.1 section 12.0

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Works through stack-frame lifetimes, escape decisions, measured allocations, and GC behavior; treats escape cases as non-exhaustive.
Examples
Contrasts value and pointer returns, benchmark allocation results, and compiler escape output.
What it assumes
Assumes familiarity with Go functions and pointers while explaining stack frames and escape analysis from concrete examples.
What you can do after
Diagnose/debug heap escapes with go build -gcflags "-m=2" and assess their allocation cost with benchmarks.
How it connects
Connects the heap to goroutine stacks, escape analysis, pointers, channels, slices, benchmarks, and garbage collection.

Also explained in

Compare how 7 books explain heap →

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.