为何Go语言将指针语义的值存储在堆上?(基于《Go实战》作者观点)
Why Do Values with Pointer Semantics Live on the Heap in Go?
Great question—let’s unpack this using William Kennedy’s framework from Go in Action as a starting point.
First, a quick recap of the semantics he laid out:
- Value semantics: Values live on the stack. When passed across program boundaries (like to another function), a full copy is created. Stack memory is super fast because it’s managed with simple pointer pushes/pops, tied directly to function execution lifecycles.
- Pointer semantics: Values are shared across boundaries via pointers. These don’t get copied—instead, multiple parts of the program reference the same underlying data.
Now, the core reason these shared values need to live on the heap boils down to memory lifecycle stability:
Stack memory is tied to function execution
When a function finishes running, its entire stack frame is immediately deallocated. If you tried to store a pointer-semantic value on the stack, any reference to it outside that function (like returning the pointer, or passing it to a long-running goroutine) would become a dangling pointer—pointing to memory that’s already been reclaimed. The heap, by contrast, is for memory that needs to outlive the function that created it.Sharing requires fixed addresses
Pointer semantics rely on multiple parts of your program referencing the same exact value. Stack memory can shift or be reused as functions exit and new ones start, so its addresses aren’t stable. Heap memory stays in place until the garbage collector (GC) marks it as unused, ensuring all pointers to the value remain valid for as long as they’re needed.Go’s escape analysis enforces this
The Go compiler runs escape analysis to figure out if a value "escapes" the current function’s stack. If a value is referenced by a pointer that leaves the function (e.g., returned, stored in a global variable, or passed to a goroutine), the compiler automatically allocates it on the heap. This is how Go prevents the dangling pointer problem I mentioned earlier—no manual memory management needed.Garbage collection handles shared memory cleanup
Since pointer-semantic values are shared across multiple parts of the program, we can’t just deallocate them when a single function finishes. The GC tracks all references to heap values and only cleans them up when there are no more pointers pointing to them. The stack can’t do this because it doesn’t track references—it just cleans up entire frames when functions exit.
In short: Pointer semantics are all about sharing data, and sharing requires memory that isn’t tied to a single function’s short lifecycle. The heap provides that stable, long-lived space, with the GC taking care of cleanup when the value is no longer needed.
William Kennedy(《Go实战》作者)指出:Go语言存在两种语义:值语义存储在栈上,指跨程序边界时会复制值;指针语义存储在堆上,指跨程序边界时会共享值。堆中未使用的内存会由垃圾回收器定期回收。
内容的提问来源于stack exchange,提问作者X. Wang

