Memory Allocator

What is it: The layer between the application and the OS that manages heap memory. malloc() finds a free block from its pool; free() returns the block. Batches mmap()/brk() syscalls into large-page pools and carves them into size classes.

Fragmentation: External fragmentation: free memory exists but in non-contiguous small blocks — a large allocation fails even though total free memory is sufficient. Accumulates in long-running servers. Internal fragmentation: allocated block is larger than requested (size classes, e.g., requesting 33 bytes gets a 64-byte slot). Bounded and predictable.

Go's allocator (TCMalloc-inspired):

graph TD alloc["Allocation request"] --> mcache["mcache: per-P free list<br/>(lock-free, fastest)"] mcache -->|"empty for this size class"| mcentral["mcentral: per-size-class shared cache<br/>(mutex-protected, refills mcache)"] mcentral -->|"empty"| mheap["mheap: global, OS-backed spans<br/>(used directly for allocations > 32KB)"]

Three tiers: - mcache: per-P (per-logical-processor) free-list per size class. Lock-free (only one goroutine per P at a time). - mcentral: per-size-class shared cache. Protected by per-mcentral mutex. Refills mcache. - mheap: global, manages large spans from the OS. Used for > 32KB allocations and to refill mcentral.

Go GC: Tricolor concurrent mark-and-sweep. Colors: white (unseen), gray (seen, children not yet scanned), black (seen, children scanned). End of marking: white objects = unreachable → swept. Write barriers handle concurrent mutation during marking. STW pauses < 1ms. GC triggered when heap reaches GOGC% above last GC size (default: doubles before GC). Tune: GOGC=200 for less-frequent GC with higher memory usage.

Real-world usage: ValkeyDB uses sync.Pool for frequently-allocated command parser buffers to reduce GC pressure.

Common pitfall

Allocating many small objects in a hot path (instead of reusing a buffer via sync.Pool or similar) doesn't just cost the allocator's time — it directly increases GC pressure, since every live allocation is something the tricolor mark phase has to visit. A hot loop that allocates a small struct per iteration can dominate CPU profile time in runtime.mallocgc and GC-related frames, showing up as "the program is slow" long before anyone thinks to check allocation counts specifically — pprof's allocation profile (not just CPU profile) is what actually surfaces this.