Go

How does a goroutine's stack grow? What changed from segmented to contiguous stacks?

Question 154HardGo 1.22 to 1.25

Every non-leaf function begins with a stack check: it compares SP with g.stackguard0. If the frame will not fit, the function calls runtime.morestack. That function allocates a new stack twice the size, copies the old stack into it, and fixes every pointer into the stack using the compiler's stack maps. The function then restarts.

  • Segmented stacks (before Go 1.3) linked new chunks onto the old stack. This caused the hot split problem: a call right at a segment boundary inside a loop allocated and freed a segment on every iteration.
  • Contiguous or copying stacks (Go 1.3+) make growth cost amortized O(1). The trade-off is that the runtime must know where every pointer into the stack is. This is one reason Go does not let you keep pointers to stack memory from C.

During GC, stacks shrink to half their size when less than a quarter is used. On 64-bit systems the maximum stack is 1 GB. Going past it gives fatal error: stack overflow ("goroutine stack exceeds 1000000000-byte limit"), and this cannot be recovered.

Gotcha: because stacks move, &local addresses can change over time. Never store uintptr copies of them.

More on Goroutines & the Scheduler

All 35 Goroutines & the Scheduler questions