Custom Memory Management
Owning allocation itself: arenas, pools, placement new and alignment
new/delete and malloc/free are general-purpose allocators: they must serve any size, any lifetime, from any thread. That generality costs time, wastes cache, and fragments memory. A custom allocator gives up generality on purpose - you know your program's allocation pattern, so you can hand back memory faster and more compactly than a general allocator ever could. This topic is about separating allocation from construction and taking control of both.
- explain why the general-purpose heap is slow and fragmenting,
- use
placement newto construct an object into memory you already own, and destroy it by hand, - build an arena (bump) allocator and a fixed-size pool, and say when each is appropriate,
- reason about alignment with
alignof,alignasandstd::align, - plug a custom memory resource into the STL with C++17
<memory_resource>.
1. Why the general heap hurts
Every call to ::operator new or malloc may do a surprising amount of work: search a free list or size-class bins for a block, split or coalesce blocks, update bookkeeping headers, and - under contention - take a lock. The returned block carries hidden per-allocation overhead (a size/header word, rounding up to a size class) and lands wherever the allocator found room, not next to its neighbours. Three costs matter for advanced code:
- Latency: a single allocation can cost hundreds of nanoseconds; in a hot loop that dominates.
- Fragmentation: after many mixed-size allocations and frees, free memory is chopped into pieces too small to reuse. External fragmentation wastes space between blocks; internal fragmentation wastes the padding inside a rounded-up block.
- Cache locality: objects that are used together but allocated far apart cause cache misses. A custom allocator can pack them contiguously.
The fix is not "write a faster malloc". It is to exploit a pattern the general allocator cannot know about: "all these objects die at the same time" (arena), or "every object is exactly the same size" (pool). When the pattern holds, the allocator collapses to a pointer bump or a linked-list pop - a few instructions, no search, no lock.