Escape Analysis & Alignment

Escape Analysis & Memory Alignment

Memory optimization usually comes down to two questions: 1. Did it hit the heap when it didn’t need to? (Excessive allocation) 2. Is it wasting space? (Poor alignment)

Part 1: Escape Analysis

“Escape Analysis” is the compiler phase that decides Stack vs. Heap.

Asking the Compiler

You don’t need to guess. Ask the compiler what it decided:

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

Output Interpretation: * can inline main: Good. * x does not escape: Great (Stack). * moved to heap: x: Bad (Heap).

Common Escape Triggers

  1. Returning Pointers: func New() *T { return &T{} } -> Escapes.
  2. Interfaces: func Log(v interface{}). The concrete type inside the interface box often escapes.
  3. Closures: Variables captured by a closure might escape if the closure outlives the function.
  4. Unknown Size: make([]int, n) always escapes if n is dynamic.

The “Mid-Stack” Trick

If you need a large buffer inside a hot loop, allocate it once outside the loop or reuse a sync.Pool. * Bad: for { b := make([]byte, 1024); process(b) } (Trash on heap every loop) * Better: b := make([]byte, 1024); for { process(b) } (One alloc) * Best (Stack): b := [1024]byte{}; for { process(b[:]) } (Stack alloc if small enough)


Part 2: Memory Alignment (Padding)

CPU reads memory in words (e.g., 64-bit / 8 bytes). If a field doesn’t align with a word boundary, the compiler adds Padding (wasted bytes).

The Struct Layout Game

Consider this struct:

type BadStruct struct {
    Flag    bool    // 1 byte
    Counter int64   // 8 bytes
    Active  bool    // 1 byte
}

Total Size: * bool (1) + padding (7) -> to align int64 * int64 (8) * bool (1) + padding (7) -> to align struct size to 8 * Total: 24 bytes

Reshuffled:

type GoodStruct struct {
    Counter int64   // 8 bytes (0-7)
    Flag    bool    // 1 byte  (8)
    Active  bool    // 1 byte  (9)
    // padding (6) -> to align struct size to multiple of 8 (16)
}

Total: 16 bytes. 33% savings just by reordering fields.

Tools used in 2026

Do not optimize manually unless you are bored. Use tools.

  • fieldalignment: bash go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest fieldalignment ./... It will report structs that use too much memory and suggest a fix.

  • betteralign: A more modern wrapper that can automatically apply changes (-apply).

When does this matter?

  • Single struct: Who cares? 8 bytes is nothing.
  • Slice of 100 million structs: 8 bytes * 100M = 800MB of RAM wasted. This triggers GC more often, costs money on cloud bills, and slows down cache.

Rule: Optimize alignment for “Data Types” (things you store in DB/Arrays). Ignore it for “Service Types” (singletons, handlers).