DevPrep
  • Interview Prep
  • Projects
  • Resources
  • Pricing
  • About Us
Submit Question
DevPrep
  • Pricing
  • About Us
Submit Question

Practice

  • JavaScript
  • DSA
  • Machine Coding
  • System Design

Resources

  • Learning Tracks
  • Articles
  • Roadmaps
  • Compare Concepts
  • Glossary
  • Developer Tools
  • All Questions

Company

  • About
  • Pricing

Legal

  • Privacy Policy
  • Terms of Service
DevPrep

© 2026 DevPrep. All rights reserved.

← Back to Questions
HardTheory

Escape Analysis: Stack vs Heap Allocation in Go

26 views

Problem Statement

Your Go microservice is generating excessive garbage, causing GC pressure. Using escape analysis, determine which allocations are escaping to the heap and optimize them to stay on the stack.

What is Escape Analysis?

The Go compiler analyzes whether a variable's lifetime extends beyond its function. If it does, the variable "escapes" to the heap. Stack allocation is faster and doesn't require GC.

Viewing Escape Analysis

go build -gcflags="-m" ./...

# More verbose:
go build -gcflags="-m -m" ./...

# Output examples:
# ./main.go:10:2: moved to heap: x
# ./main.go:15:9: &User{} escapes to heap

Common Escape Scenarios

package main

// 1. Returning pointer to local variable (ESCAPES)
func createUser() *User {
    u := User{Name: "Alice"} // Escapes to heap
    return &u
}

// 2. Storing pointer in long-lived structure (ESCAPES)
var globalUsers []*User

func addUser(name string) {
    u := &User{Name: name} // Escapes
    globalUsers = append(globalUsers, u)
}

// 3. Passing to interface{} (ESCAPES - boxing)
func logValue(v interface{}) {
    fmt.Println(v)
}

func example() {
    x := 42
    logValue(x) // x escapes (boxed into interface)
}

// 4. Closure capturing by reference (ESCAPES)
func makeCounter() func() int {
    count := 0 // Escapes
    return func() int {
        count++
        return count
    }
}

// 5. Large stack allocation (ESCAPES)
func bigArray() {
    arr := [1000000]int{} // Too large for stack, escapes
    _ = arr
}

Optimizations to Avoid Escape

// ❌ Escapes: pointer return
func newBuffer() *bytes.Buffer {
    var buf bytes.Buffer
    return &buf
}

// ✅ Stack: value return (caller allocates)
func newBuffer() bytes.Buffer {
    var buf bytes.Buffer
    return buf
}

// ❌ Escapes: interface parameter
func process(r io.Reader) { ... }

// ✅ Stack: concrete type when possible
func processFile(r *os.File) { ... }

// ❌ Escapes: slice grows unpredictably
func collect() []int {
    var result []int
    for i := 0; i < 100; i++ {
        result = append(result, i)
    }
    return result
}

// ✅ Pre-allocated: known size, less pressure
func collect() []int {
    result := make([]int, 0, 100)
    for i := 0; i < 100; i++ {
        result = append(result, i)
    }
    return result
}

Benchmark Stack vs Heap

type Data struct {
    values [100]int
}

// Stack allocation
func stackAlloc() Data {
    return Data{}
}

// Heap allocation
func heapAlloc() *Data {
    return &Data{}
}

// Benchmark results:
// BenchmarkStackAlloc   500000000   3.2 ns/op   0 B/op   0 allocs/op
// BenchmarkHeapAlloc     20000000  85.0 ns/op 896 B/op   1 allocs/op

Follow-up Questions

  1. Why does passing a value to interface{} cause escape?
  2. How large can a stack allocation be before it escapes?
  3. What is the "tiny allocator" optimization?

Sample Test Cases

Case 1
Input
func f() *int { x := 42; return &x }
Expected Output
x escapes to heap (pointer returned)
Case 2
Input
func f() int { x := 42; return x }
Expected Output
x stays on stack (value returned)

No solutions yet

Be the first to share a solution for this question.

Comments (0)

Sign in to leave a comment.

No comments yet. Be the first to comment.

Stats

Views
26
Likes
0
Solutions
0
Comments
0

Category

Backend Engineering

Languages

Go