This document describes the Go runtime's stack management system, which handles allocation, growth, shrinking, and deallocation of goroutine stacks. Each goroutine (represented by a g structure) has its own stack that grows and shrinks dynamically as needed.
For information about goroutine scheduling, see Goroutine Scheduler. For memory allocation that occurs on the heap, see Memory Management. For garbage collection interactions with stacks, see Garbage Collection.
Each goroutine's stack is represented by a stack structure that defines the memory bounds:
The actual stack memory occupies the range <FileRef file-url="https://github.com/golang/go/blob/0bf3f64c/stack.lo, stack.hi). Stacks grow downward on all architectures, meaning the stack pointer decreases as the stack grows.\n\nSources#LNaN-LNaN" NaN file-path="stack.lo, stack.hi)`. Stacks grow downward on all architectures, meaning the stack pointer decreases as the stack grows.\n\nSources">Hii
The g structure contains several stack-related fields:
| Field | Purpose |
|---|---|
stack | Actual stack memory bounds |
stackguard0 | Guard value for user Go code stack growth checks |
stackguard1 | Guard value for system stack growth checks |
stackLock | Lock for concurrent stack operations (sigprof/scang) |
The stackguard0 field is compared against the stack pointer in the function prologue generated by the compiler. It is normally set to stack.lo + StackGuard, but can be set to the special value StackPreempt to trigger preemption.
The stackguard1 field serves a different purpose depending on the goroutine type:
g0 and gsignal stacks: set to stack.lo + StackGuard~0 to trigger a crash if system stack code runs on a user stackSources: src/runtime/runtime2.go473-483
Sources: src/runtime/runtime2.go473-483
The runtime enforces maximum stack sizes that vary by architecture:
| Architecture | Maximum Stack Size |
|---|---|
| 64-bit | 1,000,000,000 bytes (1 GB) |
| 32-bit | 250,000,000 bytes (250 MB) |
An upper ceiling of 2 * maxstacksize exists to prevent crashes when SetMaxStack is called with excessive values.
Sources: src/runtime/proc.go156-168
Stacks are allocated in fixed size classes to enable efficient caching. The runtime uses multiple "orders" where each order is twice the size of the previous:
| System | FixedStack | NumStackOrders | Cached Sizes |
|---|---|---|---|
| linux/darwin/bsd | 2 KB | 4 | 2KB, 4KB, 8KB, 16KB |
| windows/32 | 4 KB | 3 | 4KB, 8KB, 16KB |
| windows/64 | 8 KB | 2 | 8KB, 16KB |
| plan9 | 4 KB | 3 | 4KB, 8KB, 16KB |
Stacks larger than the maximum cached size are allocated directly from the heap. Each P (processor) maintains a per-order cache of up to 32 KB worth of stack segments per size class.
Sources: src/runtime/malloc.go135-150
Stack allocation is implemented by the stackalloc function, which uses a hierarchical caching strategy to minimize allocation overhead.
Stack Allocation Flow
Sources: src/runtime/stack.go339-451 src/runtime/proc.go152-295
The stack allocator uses three levels of caching:
| Level | Storage | Lock Required | Purpose |
|---|---|---|---|
| P-local cache | p.stackcache[order] | No | Fast path for small stacks |
| Global pool | stackpool[order] | Yes (stackpool[].item.mu) | Shared pool across Ps |
| OS allocation | sysAlloc | Yes (heap lock) | Fresh memory from OS |
For stacks larger than _FixedStack (typically 2-8 KB), allocation bypasses the cache and goes directly to the OS via sysAlloc.
Constants:
_StackCacheSize = 32 KB (maximum cached per order per P)_NumStackOrders = 2-4 (varies by OS/architecture)_FixedStack = 2-8 KB (minimum stack size)Sources: src/runtime/stack.go67-140 src/runtime/malloc.go135-150
Stack growth is triggered when the compiler-generated function prologue detects potential overflow. The compiler inserts a comparison of the stack pointer against g.stackguard0. When the condition is met, the function calls the runtime's morestack routine (implemented in assembly).
Stack Growth Detection Flow
Sources: src/runtime/stack.go20-67 src/runtime/stack.go935-1087
The newstack function coordinates stack growth:
Key Steps:
Size calculation: New stack size is computed based on requested frame size and current stack. Typically doubles the stack size, but respects minimum (_FixedStack) and maximum (maxstacksize) limits.
Stack allocation: Calls stackalloc(newsize) to obtain new stack memory.
Stack copying: The copystack function copies live data from old to new stack. This function:
Sudog adjustment: Calls adjustsudogs(gp) to update sudog structures in channel wait queues that reference stack addresses. The g.activeStackChans flag indicates whether this step is necessary.
Old stack deallocation: Returns old stack via stackfree(oldstack).
Execution resumption: Uses gogo(&gp.sched) to resume execution on the new stack.
Special Handling:
| Case | Field/Flag | Action |
|---|---|---|
| Preemption request | g.preempt = true | Call gopreempt_m() instead of growing |
| Active channels | g.activeStackChans = true | Acquire channel locks during adjustsudogs |
| No splitting allowed | g.throwsplit = true | Throw fatal error instead of growing |
Sources: src/runtime/stack.go935-1087 src/runtime/stack.go795-920
The stackguard0 field serves a dual purpose. In addition to detecting stack overflow, it can be set to the special value StackPreempt to signal that the goroutine should be preempted:
Sources: src/runtime/runtime2.go473-483 src/runtime/runtime2.go514-516
Stack shrinking is initiated during garbage collection to reclaim unused stack space. The GC scans goroutine stacks via scanstack and may set the g.preemptShrink flag when detecting underutilization.
Stack Shrinking Flow
Sources: src/runtime/stack.go1090-1154 src/runtime/mgcmark.go1291-1345
The shrinkstack function performs the actual shrinking operation:
_FixedStackcopystack(gp, newsize) to copy to smaller stackstackfree(oldstack)Shrinking is skipped if:
_FixedStack)throwsplit flag setSources: src/runtime/stack.go1090-1154
Sources: src/runtime/runtime2.go516
The stack guard mechanism provides overflow detection without requiring a check on every instruction. The guard is positioned _StackGuard bytes above the bottom of the stack (stack.lo). The compiler generates different prologue code based on frame size:
Frame Size Categories:
| Frame Size | Constant | Check Strategy |
|---|---|---|
≤ _StackSmall | 128 bytes | Simple compare: SP vs stackguard0 |
_StackSmall to _StackBig | ~4 KB | Compare: SP - framesize + _StackSmall vs stackguard0 |
≥ _StackBig | ~4 KB | Unconditional call to morestack |
Functions with frames ≤ _StackSmall are allowed to protrude up to _StackSmall bytes below the guard without triggering overflow, optimizing the common case of small stack frames.
Key Constants (from stack.go):
_StackSystem = GOOS-dependent (e.g., 0 for most, 512 for Windows)
_StackGuard = 928 bytes (Linux) or varies by OS
_StackSmall = 128 bytes
_StackBig = 4096 bytes
Special Values:
| Value | Purpose |
|---|---|
stackguard0 = stack.lo + _StackGuard | Normal overflow detection |
stackguard0 = stackPreempt | Triggers preemption check instead |
Sources: src/runtime/stack.go68-140
Different goroutine types use different stackguard0 and stackguard1 configurations:
User Goroutines (regular g):
stackguard0 = stack.lo + _StackGuard (or stackPreempt for preemption)
stackguard1 = stackFork (~0, causes crash on misuse)
System Goroutines (g0, gsignal):
stackguard0 = stack.lo + _StackGuard
stackguard1 = stack.lo + _StackGuard
The stackguard1 field serves as a safety mechanism:
stackFork (~0), which causes a crash if runtime code that requires the system stack accidentally runs on a user stackThis configuration is set up in mcommoninit for M's system goroutines and in newproc1 for user goroutines.
Sources: src/runtime/runtime2.go473-483 src/runtime/proc.go1002-1041
Each M (machine/OS thread) has a special g0 goroutine whose stack is used for:
The g0 stack is typically allocated by the OS as the thread's default stack, making it safe to run C code compiled by gcc.
Sources: src/runtime/runtime2.go619
The gsignal goroutine has a dedicated stack for signal handling. This stack is separate from both user stacks and the g0 stack to ensure signal handlers can run even when other stacks are in inconsistent states.
The goSigStack field in the M structure holds the Go-allocated signal handling stack if the platform requires it.
Sources: src/runtime/runtime2.go626-627
Sources: src/runtime/runtime2.go619 src/runtime/runtime2.go626-627
When a stack is copied during growth or shrinking, all pointers to the old stack must be updated. This includes:
The activeStackChans flag on the g structure indicates whether any unlocked channels point into the goroutine's stack. If set, stack copying must acquire channel locks to protect these regions.
Sources: src/runtime/runtime2.go525-530
The runtime uses stack maps (generated by the compiler) to identify which stack slots contain pointers. During stack copying:
Sources: src/runtime/runtime2.go525-530
Each processor (P) maintains a cache of free stacks to minimize allocation overhead:
Sources: src/runtime/malloc.go135-150
Each cache entry can hold up to _StackCacheSize (32 KB) worth of stacks for that size class. When the cache is full, stacks are returned to the global pool. When the cache is empty, stacks are obtained from the global pool or allocated fresh from the OS.
Sources: src/runtime/malloc.go135-150
During garbage collection, the runtime scans goroutine stacks to find live pointers. This scanning:
preemptShrink if the stack is underutilizedThe gcscandone flag in the g structure tracks whether a stack has been scanned during the current GC cycle.
Sources: src/runtime/runtime2.go524
Asynchronous preemption uses stack guards as a signaling mechanism:
g.preempt = trueg.stackguard0 = StackPreemptstackguard0g.preempt is setThe preemptStop flag indicates whether to transition to _Gpreempted state or just deschedule.
Sources: src/runtime/runtime2.go514-515
When calling into C code, the runtime switches to the M's g0 stack:
cgocall function switches from the current goroutine's stack to g0This ensures that C code, which may not respect Go's stack conventions, operates on a stable, OS-allocated stack.
Sources: src/runtime/runtime2.go619
When a stack overflow is detected, the runtime can respond in several ways depending on context:
maxstacksize, panicstackguard1 = ~0)The badmorestackg0 and badmorestackgsignal functions handle the error cases for non-growable stacks.
Sources: src/runtime/proc.go590-605 src/runtime/proc.go607-611
Several debug flags and test functions assist with stack debugging:
stackPoisonCopy: Poisons old stack memory after copying to detect use-after-freethrowsplit: Flag on g that prevents stack splitting (for testing)ShrinkStackAndVerifyFramePointers: Test function that shrinks stack and verifies frame pointersSources: src/runtime/export_test.go435-450 src/runtime/runtime2.go525
Refresh this wiki