Custom Allocators: Pools, Arenas, Slabs
This chapter covers the three most important custom allocation strategies: pools, arenas, and slab allocators. These replace general-purpose malloc when you need deterministic behavior, less fragmentation, or faster allocation.
Why This Matters
General-purpose allocators are general. Custom allocators exploit knowledge of your allocation patterns (fixed sizes, short lifetimes, bulk cleanup) to be faster, more predictable, and less fragmented — essential in embedded, games, and high-performance systems.
Prerequisites
c.memory.2— malloc/calloc/realloc/free.
Core Concept
Pools
A pool (or fixed-size pool) allocates objects of a single size from a preallocated block. Allocation is O(1) (pop from a free list), no fragmentation for that size, and no per-object metadata overhead beyond the free-list pointer.
Arenas
An arena (or bump/linear allocator) hands out memory by bumping a pointer forward. Individual objects are never freed; the entire arena is reset at once. Ideal for short-lived, group-lifetime data (a parser, a frame, a request).
Slabs
A slab allocator groups objects of the same size into contiguous slabs and manages them together. It reduces fragmentation and improves cache locality. It is the strategy behind many kernel allocators.
Examples
Bump arena
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct {
unsigned char *base;
size_t size;
size_t used;
} Arena;
static size_t align_up(size_t n, size_t a)
{
return (n + a - 1) & ~(a - 1);
}
void *arena_alloc(Arena *a, size_t size, size_t align)
{
size_t off = align_up(a->used, align);
if (off + size > a->size)
return NULL;
void *p = a->base + off;
a->used = off + size;
return p;
}
void arena_reset(Arena *a)
{
a->used = 0;
}
Fixed-size pool (free list)
typedef struct Block {
struct Block *next;
} Block;
typedef struct {
Block *free_list;
size_t block_size;
} Pool;
void *pool_alloc(Pool *p)
{
if (p->free_list == NULL)
return NULL;
Block *b = p->free_list;
p->free_list = b->next;
return b;
}
void pool_free(Pool *p, void *ptr)
{
Block *b = ptr;
b->next = p->free_list;
p->free_list = b;
}
The free list stores the next pointer *inside* each free block, so no extra per-object allocation is needed.
How It Works
A bump arena advances a cursor and never reclaims individual objects; reset moves the cursor back to zero. A pool keeps a singly-linked list of free blocks, pushing and popping in O(1). A slab allocator partitions memory into same-size objects and tracks which are free, often with a bitmap.
Variations
Thread-local arenas
Give each thread its own arena to avoid lock contention; merge/reclaim at the end of a phase.
Stack-like arenas
Support arena_mark/arena_restore so you can roll back to a previous cursor, freeing a whole suffix of allocations at once.
Common Mistakes
- Forgetting alignment in a bump arena.
- Using arena-allocated memory after
arena_reset(dangling). - Not checking pool exhaustion.
- Mixing
freewith custom-allocated pointers (never callfreeon memory
not from malloc).
Undefined Behavior
- Using a pointer after an arena reset (lifetime ended).
VERIFIED - Calling
freeon a pointer from a custom allocator.VERIFIED - Misaligned allocation (if the allocator does not align correctly).
Portability
- Custom allocators are standard C code, but their performance and memory
source (e.g., mmap vs. a static buffer) may be platform-specific.
Under the Hood
A bump arena is a few pointer additions. A pool is a linked-list push/pop. A slab allocator is a bitmap plus arithmetic. All avoid the general allocator's search and coalescing costs, which is why they are faster and more predictable.
Practical Usage
- Use a bump arena for a phase of computation with many small, short-lived
objects (parsing, building an AST).
- Use a pool for many fixed-size objects (nodes, packets, particles).
- Use slabs when you need predictable latency and low fragmentation.
Exercises
1. Implement a bump arena with arena_alloc and arena_reset, and test it. 2. Implement a fixed-size pool with pool_alloc/pool_free. 3. Write a small test that allocates many objects from a pool and frees them; verify no leaks and O(1) behavior. 4. Explain why you must not call free on arena/pool pointers.
Deep Challenge
Implement a slab allocator that serves three size classes (e.g., 16, 64, 256 bytes) from a large block, using a free list per size class. Explain the trade-offs vs. a single-size pool and a bump arena.
Related Concepts
c.memory.2— malloc family.c.mem.fragmentation— why custom allocators reduce fragmentation.c.mem.embedded-sram— allocators in embedded.
References
- ISO/IEC 9899:2018 §7.22.3 (allocation), plus allocator literature
(e.g., "slab allocator" in kernel design).
Verification
- Bump/pool/slab designs are conventional, not standard-library features.
VERIFIED (as correct C code)
- Mixing
freewith custom allocators is UB.VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Bump arena
- [ ] Fixed-size pool
- [ ] Slab allocator
- [ ] Alignment in custom allocators
- [ ] Arena reset lifetime
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.mem.custom-alloc | 0 | 6 |
| c.mem.pool | 0 | 6 |
| c.mem.arena | 0 | 6 |
| c.mem.slab | 0 | 5 |