Go’s conditionals and loops are minimalist by design, but their simplicity hides precise memory decisions. Let’s explore how if, switch, and for interact with the stack, heap, and data segments—and where hidden allocations creep in.
The Code
package main
func main() {
// Conditional with stack-allocated variable
if score := 42; score > 50 {
// ...
}
// Loop with slice iteration
nums := []int{10, 20, 30} // Slice header (stack), backing array (heap)
for i, n := range nums {
// ...
}
// Loop closure capturing variable
for _, n := range nums {
func() {
println(n) // n escapes to heap
}()
}
}Memory Segmentation Visualized
+-------------------+ Lowest Address
| Code |
|-------------------|
| main() | // Compiled logic for conditionals/loops
| range handling | // Machine code for slice iteration
+-------------------+
| Data |
|-------------------|
| (No constants) |
+-------------------+
| Stack |
|-------------------|
| score (int = 42) | // if-scoped variable
| nums (slice) | // Pointer to heap array
| i, n (loop vars) | // Per-iteration copies
+-------------------+
| Heap |
|-------------------|
| []int backing | // nums' array [10, 20, 30]
| n (int copies) | // Captured by closure (one per iteration)
+-------------------+ Highest AddressBreakdown of Memory Behavior
1. Conditionals (if, switch)
-
Variables declared in
if/switch:-
scoreinif score := 42; ...is stack-allocated. - Scoped to the block, cleaned up immediately.
-
- No implicit heap allocations unless variables escape (e.g., passed to a closure).
2. Loops (for, range)
-
Loop variables:
-
iandninfor i, n := range numsare reused per iteration (stack-allocated). - Values are copied, so modifying
ndoesn’t affect the original slice.
-
-
Slice backing array:
-
nums := []int{10, 20, 30}stores the slice header (pointer, len, cap) on the stack. - The actual array
[10, 20, 30]lives on the heap.
-
3. Closures in Loops
-
Captured variables:
-
nin the closurefunc() { println(n) }()escapes to the heap. - Each iteration’s
nis copied to the heap to outlive the loop iteration.
-
- Verify with escape analysis:
go build -gcflags="-m" main.go
# ./main.go:12:3: ... n escapes to heapWhy This Matters
-
Loop variable reuse: Go reuses the same memory address for
iandnin each iteration.-
Gotcha: Goroutines in loops capturing loop variables (e.g.,
go func() { ... }()) will capture the latest value ofi/n. Fix:
for _, n := range nums { n := n // Create a stack copy per iteration go func() { println(n) }() } -
Gotcha: Goroutines in loops capturing loop variables (e.g.,
Slice efficiency: Large slices force heap allocations for backing arrays. Pre-size with
maketo avoid reallocations.
Optimization Tips
- Avoid closures in hot loops: Use explicit parameters to keep variables on the stack.
- Pre-allocate slices:
data := make([]int, 0, 1000) // Heap-allocated but avoids reallocations- Beware of interface{} in conditions:
var val interface{} = 42 // Heap escape
if val == 42 { ... } // Type check overheadKey Insight: Go’s for loops reuse variables to minimize allocations—a design choice that’s fast but demands caution with closures and goroutines.