Photo by Umberto on Unsplash

Slab Allocation: Fixed-Size Memory Pools for High-Performance Systems


Memory allocation is one of the most frequent operations in any running system, and general-purpose allocators like malloc must handle requests of arbitrary size. This flexibility comes at a cost: metadata overhead, fragmentation, and lock contention. Slab allocation offers a different approach, trading generality for speed and predictability by pre-allocating memory in fixed-size chunks.

How Slab Allocators Work

A slab allocator divides memory into caches, each dedicated to objects of a specific size. Within each cache, memory is organized into slabs—contiguous blocks of memory subdivided into equally sized slots. When a request comes in for an object of a given size, the allocator finds the appropriate cache and returns a free slot from one of its slabs. When the object is freed, the slot is marked available for reuse.

This design eliminates the need to search for a suitably sized block or coalesce freed memory. Allocation becomes a constant-time operation: pop a slot from a free list. Deallocation is equally fast: push the slot back onto the list.

Eliminating Fragmentation

General-purpose allocators suffer from external fragmentation, where free memory exists but is scattered into unusable pieces. Over time, this forces allocators to request more memory from the kernel even when sufficient total memory is free.

Slab allocators sidestep this problem. Because every object in a cache is the same size, freed slots can always be reused for future allocations of that size. There are no gaps too small to use, and no need to split or merge blocks.

Internal fragmentation still exists—if you allocate a 96-byte object from a 128-byte slab, you waste 32 bytes—but the overhead is bounded and predictable. In practice, systems define caches for common sizes, so most allocations fit tightly.

Cache-Friendly and Kernel Integration

Slab allocators were originally developed for operating system kernels, where the same types of objects—inodes, dentries, task structs—are allocated and freed continuously. The Linux kernel’s SLUB allocator is a modern slab allocator that powers most kernel memory management.

Because slabs are contiguous and objects are uniformly sized, they pack tightly in CPU caches. Allocating and freeing the same type of object repeatedly keeps the relevant metadata and memory pages hot, improving cache hit rates.

Some slab allocators also support object constructors and destructors, pre-initializing objects when a slab is created rather than on every allocation. This further reduces per-operation overhead.

Concurrency and Per-CPU Caches

Traditional allocators require global locks to protect shared metadata, creating contention under heavy concurrent load. Slab allocators mitigate this by maintaining per-CPU caches of free slots. Each CPU can allocate and free objects from its local cache without coordination, only falling back to a global pool when its cache is exhausted or overflowing.

This design scales naturally with core count and makes allocation latency more predictable, since most operations avoid lock acquisition entirely.

Limitations and Trade-Offs

Slab allocators excel when object sizes are known and consistent, but they are not general-purpose solutions. Applications with highly variable allocation sizes will waste memory by over-provisioning caches or suffer from internal fragmentation.

Slab allocators also consume more memory upfront, since they pre-allocate entire slabs even if only a few objects are in use. This is acceptable in kernel space, where object types are well-defined, but may be inefficient for user-space workloads with unpredictable demand.

Where Slab Allocation Appears

Beyond the kernel, slab-like allocation strategies appear in many high-performance systems. Object pools in application servers, packet buffer pools in network stacks, and region-based allocators in compilers all follow the same principle: partition memory by size or lifetime, and recycle aggressively.

Slab allocation is a reminder that sometimes the best way to make allocation fast is to stop generalizing and start specializing.