Explain the SLUB (SLAB Unreliable Allocator) memory allocator in the Linux kernel

Explain the SLUB (SLAB Unreliable Allocator) memory allocator in the Linux kernel

SLUB is the default memory allocator in the Linux kernel today, sitting quietly underneath nearly every kmalloc() call on every mainstream distribution you’ve probably ever used. I want to explain, in real depth, what SLUB is, how it’s structured internally, why it replaced its predecessor, and how to actually observe and tune it on a running system.

One quick housekeeping note before diving in: SLUB is often mis-expanded as “SLAB Unreliable Allocator.” That’s not correct and it’s worth correcting, because it’s a common source of confusion in study material. SLUB officially stands for “the unqueued slab allocator” — “unqueued” referring to its removal of SLAB’s per-CPU/per-node queue-based object tracking in favor of a leaner design. Nothing about it is “unreliable”; if anything, it’s the more robust and better-instrumented of the kernel’s slab allocators.

Why SLUB Exists: The Problem With SLAB

The original SLAB allocator (designed by Jeff Bonwick’s slab allocator concept, adapted for Linux) worked well for a long time. It grouped kernel objects into caches, kept per-CPU queues of free objects for fast allocation, and tracked color offsets to reduce cache-line contention. But as core counts climbed into the dozens and eventually hundreds, SLAB’s queuing structures — arrays of pointers per CPU per cache — became a real scalability and memory-overhead problem. Managing all those queues also made SLAB’s code complex and harder to maintain.

Christoph Lameter’s SLUB, merged into the kernel around 2007, threw out the queues entirely. Instead of pre-populated free-object arrays, SLUB tracks free objects as a singly-linked free list embedded directly inside the unused objects themselves, and it leans heavily on per-CPU “current slab” pointers rather than queues of pre-cached objects.

Core SLUB Data Structures

To actually understand SLUB, you need to know three levels of structure:

kmem_cache (e.g. "kmalloc-64")
   |
   +-- per-CPU "cpu slab" (fast path, lock-free-ish)
   |
   +-- per-node partial list (slabs with some free objects)
   |
   +-- full slabs (tracked, no free objects)
  1. kmem_cache — represents one size class or a custom cache created via kmem_cache_create(). It knows the object size, alignment, and constructor/destructor if any.
  2. Slab (a page or contiguous set of pages) — divided evenly into fixed-size object slots. Free slots form an in-place singly linked list, meaning the “next free pointer” is literally stored inside the unused object’s own memory — a clever trick that avoids needing separate metadata arrays.
  3. Per-CPU slab pointer — each CPU keeps a pointer to its “current” slab for a given cache. Allocation from this slab requires no locking in the common case, just an atomic compare-and-swap on the freelist head.

The Fast Path: Allocation Without Locks

This is the part that makes SLUB fast. When a thread calls kmalloc():

  1. The kernel computes which size-class cache to use.
  2. It checks the calling CPU’s current slab for that cache.
  3. If the current slab has a free object, SLUB pops it off the in-slab freelist using a lock-free cmpxchg operation and returns it. No spinlock is taken in the common case.
  4. If the current slab is empty, SLUB falls back to the per-node partial list — slabs that are neither completely full nor completely empty — grabs one, makes it the new per-CPU current slab, and retries the fast path.
  5. If no partial slab is available, SLUB allocates a fresh slab from the buddy allocator, carves it into objects, and proceeds.

Freeing an object follows roughly the reverse path: if it’s freed back to the CPU’s own current slab, it’s a fast lock-free push back onto the freelist. If it’s freed from a different CPU than the one that allocated it (very common with kernel objects passed between cores), SLUB uses a remote free path involving an atomic operation on the slab’s own freelist, which is a bit more expensive but still avoids a full lock in most cases.

Size Classes

SLUB pre-creates a family of general-purpose caches for common sizes: 8, 16, 32, 64, 96, 128, 192, 256, 512, 1024, 2048, 4096 bytes, and beyond, up to KMALLOC_MAX_SIZE. A kmalloc(50) call gets rounded up to the nearest available class — 64 bytes in this case — which is the internal fragmentation cost mentioned earlier.

Subsystems that allocate a specific object type repeatedly and care about tighter packing or need a constructor/destructor call kmem_cache_create() to make a dedicated cache — task_struct, inode_cache, dentry, and networking’s sk_buff are classic examples. This avoids rounding waste for high-frequency, fixed-size kernel objects.

Slab Consolidation and Memory Reclaim

SLUB actively tries to keep object density high:

  • New allocations prefer partial slabs over freshly carved ones, filling existing gaps before creating new ones.
  • When a slab becomes completely empty, it’s returned to the buddy allocator (subject to some hysteresis controlled by min_partial, which keeps a small number of empty slabs around briefly to avoid thrashing under alloc/free cycling).
  • Under memory pressure, the kernel’s shrinker infrastructure can ask SLUB-backed caches (like the dentry and inode caches) to release unused objects.

Debugging Features

This is one of SLUB’s genuine strengths over SLOB and honestly a big improvement over classic SLAB too. Compiled with the right options, SLUB supports:

  • Redzoning — extra bytes around each object to catch buffer overruns.
  • Poisoning — filling freed memory with a known pattern (0x6b, colloquially “kfree poison”) to catch use-after-free bugs.
  • Object and slab tracking — recording allocation/free call stacks for leak detection.
  • Integration with KASAN (Kernel Address Sanitizer) for much finer-grained memory-error detection during development and fuzzing.

These are enabled via boot parameters like slub_debug=FZPU or kernel config options, and they’re indispensable when chasing memory corruption bugs in kernel modules or drivers.

Observing SLUB on a Running System

$ sudo slabtop
$ cat /proc/slabinfo | head
$ ls /sys/kernel/slab/          # per-cache tunables and stats
$ cat /sys/kernel/slab/kmalloc-64/objects

/sys/kernel/slab/<cache-name>/ exposes a wealth of tunables — order (how many pages per slab), min_partial, cpu_partial — that let you tune behavior for specific high-churn caches on specialized workloads (databases, high-throughput network appliances, etc.).

SLUB vs SLAB vs SLOB — Quick Comparison

FeatureSLABSLUBSLOB
Per-CPU queuesYes (array-based)No (in-place freelist)No
Multi-core scalabilityModerateExcellentPoor
Debugging supportBasicExtensive (KASAN, redzone, poison)Minimal
Memory overheadHigherLowLowest
StatusRemoved (Linux 6.5)Default, actively maintainedRemoved (Linux 6.4)

Practical Example: Tracing an Allocation Hotspot

Say a server is under heavy memory pressure and slabtop shows the dentry cache ballooning. A reasonable investigation path:

  1. slabtop -o to get a one-shot sorted snapshot.
  2. cat /proc/sys/vm/drop_caches — write 2 to drop reclaimable slab caches (dentries, inodes) and see if the cache shrinks, confirming it’s reclaimable rather than pinned.
  3. Check for file-descriptor or path-lookup-heavy workloads (build systems, find over huge trees) that would explain dentry churn.
  4. If the cache doesn’t shrink even after drop_caches, look for a reference-count leak in whatever subsystem holds those dentries pinned.

Best Practices

  • Leave SLUB’s defaults alone unless profiling shows a genuine bottleneck — its self-tuning per-CPU behavior handles the overwhelming majority of workloads well.
  • For custom kernel modules allocating many identical objects, use kmem_cache_create() with an appropriate constructor instead of raw kmalloc() in a loop — it reduces initialization cost and improves cache locality.
  • Enable slub_debug only in development/staging kernels; it adds real overhead unsuitable for production.
  • Watch /proc/buddyinfo alongside slabtop when diagnosing fragmentation — SLUB’s slab-level health doesn’t tell the whole story if the underlying buddy allocator itself is fragmented.

Summary

SLUB earned its place as the Linux kernel’s default general-purpose memory allocator by trading SLAB’s complex per-CPU queuing structures for a leaner, mostly lock-free, in-place freelist design that scales far better on modern multi-core hardware. Its size-class system keeps internal fragmentation reasonable, its partial-slab-first allocation policy fights external fragmentation, and its rich debugging instrumentation makes it a genuinely good citizen for kernel developers chasing memory bugs. If you’re running a Linux system built any time in roughly the last decade, SLUB is almost certainly the allocator quietly doing the work behind the scenes.

FAQs

What does SLUB actually stand for? The unqueued slab allocator — a reference to its removal of SLAB’s per-CPU queue structures, not “SLAB Unreliable Allocator,” which is a common misconception.

Is SLUB the default allocator in modern Linux? Yes, it has been the default since SLAB’s removal, and it’s used across essentially all mainstream distributions.

Does SLUB support debugging tools like KASAN? Yes, SLUB has extensive debugging support including redzoning, poisoning, object tracking, and KASAN integration.

How is SLUB different from SLOB? SLUB uses fixed size-class slabs with per-CPU fast paths for speed and scalability; SLOB uses a simple linked list of free blocks optimized for minimal memory footprint on tiny embedded systems.

References

  • Lameter, C., “The SLUB Allocator,” Linux kernel documentation (Documentation/mm/slub.rst)
  • Linux kernel source, mm/slub.c
  • LWN.net, “The SLUB allocator” and related kernel memory management articles
  • Kernel.org, Documentation/vm/slub.txt / slabinfo manpage-style documentation
Total
0
Shares

Leave a Reply

Previous Post
What are memory allocators, and why are they crucial for kernel memory management

What are memory allocators, and why are they crucial for kernel memory management

Next Post
What is the SLOB (Simple List Of Blocks) memory allocator in the Linux kernel

What is the SLOB (Simple List Of Blocks) memory allocator in the Linux kernel

Related Posts