In the previous article we explored how the Go runtime bootstraps itself — how a Go binary goes from the operating system handing it control to your func main() running. During that bootstrap, one of the first things the runtime sets up is the memory allocator. And that’s what we’re going to explore today.
Think of the memory allocator as a warehouse manager. Your program constantly needs boxes of different sizes — sometimes tiny, sometimes huge — and it needs them fast. The allocator’s job is to hand out those boxes as quickly as possible, keep the warehouse organized so nothing goes to waste, and work with the garbage collector to reclaim boxes that nobody is using anymore.
But before we get into the warehouse itself, let’s talk about when things actually end up there.
When Does Memory Allocation Happen?
Not every variable in your program goes through the memory allocator. Go has two places to put data: the stack and the heap.
The stack is the easy one. Each function call gets its own little scratch space on the stack, and when the function returns, that space is automatically gone. It’s fast and simple — no bookkeeping needed.
But sometimes data needs to stick around after the function that created it is done. Maybe you’re returning a pointer to something, or storing a value that other parts of your program will use later. That data can’t live on the stack — it would vanish when the function returns. So it goes on the heap, which is a longer-lived region of memory.
The Go compiler is actually pretty smart about this. It analyzes your code at compile time to decide what can stay on the stack and what needs to go on the heap — this is called escape analysis (we covered it in detail in the IR article ).
Every time something ends up on the heap, that’s when the memory allocator comes into play. It’s the system that finds free space on the heap and hands it over. And that’s what the rest of this article is about.
A small simplification: the picture above is not the whole story. In Go, goroutine stacks are actually allocated from the heap — so the memory allocator provides the space where stacks live. But once a stack is allocated, the variables on it are managed very differently from heap objects: they’re just offsets within the stack frame, with no allocator involvement per variable. So while the allocator is responsible for the stack memory, it’s not involved in placing individual variables on the stack. For this article, we’ll focus on the heap side of things.
... continue reading