Golang / Golang Internals and Memory Management Interview Questions
How does Go's memory allocator work? Explain mcache, mcentral, and mheap.
Go uses a hierarchical, size-class-based allocator inspired by TCMalloc (Thread-Caching Malloc). The three levels minimise lock contention and fragmentation.
| Layer | Scope | Locking | Purpose |
|---|---|---|---|
| mcache | Per-P (per logical CPU) | Lock-free | Per-CPU cache of spans for each size class — fast path |
| mcentral | Per size class, global | Mutex per size class | Central pool of spans; supplies mcache when empty |
| mheap | Global | Mutex (large objects) | OS memory; supplies mcentral with new spans; manages large (>32 KB) allocations directly |
// Allocation path for a small object (<=32 KB): // 1. Round up to nearest size class (e.g., 24 bytes â 32-byte class) // 2. Check P's mcache for that size class â if span available, use it (no lock) // 3. If mcache empty, fetch a span from mcentral (mutex on that size class) // 4. If mcentral empty, request memory from mheap (global lock) // 5. If mheap exhausted, request OS memory via mmap/VirtualAlloc // Large object allocation (>32 KB): // Directly from mheap â allocated as a multi-page span // Tiny allocation (<=16 bytes, no pointers, not zero-sized): // Multiple tiny objects packed into a single 16-byte slot // e.g., allocating many bool or small int values is very cheap // Size classes â Go has ~68 size classes: // 8, 16, 24, 32, 48, 64, 80, 96, 112, 128, ... 32768 bytes // Inspecting allocation stats var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Printf("Alloc = %d KB\n", m.Alloc/1024) fmt.Printf("TotalAlloc = %d KB\n", m.TotalAlloc/1024) fmt.Printf("Sys = %d KB\n", m.Sys/1024) fmt.Printf("Mallocs = %d\n", m.Mallocs) fmt.Printf("Frees = %d\n", m.Frees)
The size-class design eliminates fragmentation for small objects: each span serves exactly one size class, so all objects in a span are the same size and fit perfectly. This avoids the fragmentation of a general-purpose allocator.
More Related questions...