a common mistake when using Goroutine Vs Thread
Fast recall
Quick revision
Core answer
Practical understanding of Goroutine Vs Thread.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
The 2KB start: goroutines begin with a small contiguous stack — enough for a typical call chain — because most goroutines never need more, and starting small is what makes 'a goroutine per request, per connection, per item' affordable at 100k+ scale.
Growing: the runtime checks stack space at function entry (a small prologue comparing the stack pointer to a guard). When a call would overflow, the runtime:
- 1Allocates a new stack — typically double the current size (2KB → 4KB → 8KB...).
- 2Copies the entire existing stack contents to the new block.
- 3Rewrites all pointers INTO the stack (locals, spill slots) to the new addresses.
- 4Frees the old block and continues.
The whole operation is transparent — running code has no idea its stack moved. This works precisely because Go's runtime knows everything about the stack: it controls the frame layout, tracks pointer-containing slots (that's what escape analysis and the frame-pointer maps are FOR), and can therefore adjust every reference. C can't do this — stacks in C are fixed-at-create because arbitrary pointers into them exist and can't be found.
The shrink: when a deep call returns, a goroutine's stack can shrink back down (a GC-time optimization), so a goroutine that briefly went deep doesn't hold that memory forever.
The consequence for interview answers: goroutines cost 2KB + whatever your call depth actually grows to — the initial number is a floor, not a cap. Deep recursion costs real memory per goroutine, same as threads, but the common case (shallow handlers, parked waits) stays near the floor — which is exactly the pattern the concurrency style encourages.
// starts ~2KB; grows on demand:
func deep(n int) int { if n == 0 { return 0 }; return deep(n-1) + 1 }
// deep(10) on a fresh goroutine: stack grew for 10 frames
// deep(100000): stack grew for 100k frames — same goroutine, bigger stacka common mistake when using Goroutine Vs Thread
Direct Answer
Goroutines start at ~2KB with a contiguous, growable stack: when a function call needs more, the runtime allocates a bigger block, copies the stack over, and redirects — all invisible to the code.
Deep Dive & Architecture
Why 2KB is safe: Go's calling convention and runtime cooperate to make every stack frame inspectable. Each function's prologue (compiler-inserted) compares the stack pointer against the guard — the limit of the current stack block. Room enough: the call proceeds. Not enough: trigger growth.
Growth in detail: allocate a new block (double the size — amortized same reasoning as slice growth), copy the ENTIRE old stack into the new one (the deep-call frames included), then — the step that makes this possible and that C cannot do — fix up every pointer that pointed into the old stack: locals holding addresses of other locals, spill slots with pointer maps. The runtime knows exactly which words are pointers (the compiler emitted that map), so it can find and rewrite them all. Then the function's frame is re-entered on the new stack and execution continues as if nothing happened.
Why threads can't do this: C and most runtimes allow unrestricted pointers into the stack (address of a local escaping into a global). No metadata says which words in the stack are pointers — so no runtime can find the references to rewrite. Grow-in-place tricks (guard pages, jumping stacks) exist but are partial; the general solution is 'allocate enough at creation' — megabytes per thread.
The numbers that matter: 100k goroutines at ~2KB committed = ~200MB of stacks (and most never grow past the start — shallow handlers, parked waits). 100k OS threads at even 1MB = 100GB of address space reserved and tens of GB committed: the machine dies long before the program runs. The growth mechanism is what lets Go gamble on 2KB — the bet is that most goroutines never go deep, and the runtime only pays for the ones that do.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Goroutine Vs Thread, and when would you use it?
- When would you use Goroutine Vs Thread in a real project?
- How would you compare Goroutine Vs Thread with an alternative?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.