Memcached slab classes and the -f factor: design boundaries for predictable eviction
When memcached evicts items unexpectedly, the root cause is often an unexamined slab class size or -f factor. This article maps the memory layout, operational checks, and failure modes so you can size cache with confidence.
19 Aug 2026, 21:08 UTC

The problem that prompted this note
You've deployed memcached, started feeding traffic, and keys vanish before their TTL expires. The service reports available memory, yet evictions happen unpredictably. Often the culprit isn't overall capacity but an unexamined slab class size or the -f memory factor. The useful takeaway: by understanding how memcached maps item sizes to fixed slab classes, you can configure -f and maximum item size to avoid surprise evictions and keep cache behavior predictable.
Requirements and the smallest suitable design
Memcached divides configured memory into slab classes, each backing a linked list of item chunks of a fixed size. The smallest slab class holds chunks of 88 bytes (on 64-bit builds), and each subsequent class grows by a multiplier determined at startup. Objects up to 1 MB can be stored without fragmentation, because each size class has its own bucket chain, allowing O(1) allocation. To build the smallest suitable design, you need two numbers: total memory -m and the chunk size multiplier -f. The multiplier defaults to 1.25, meaning each slab class's chunk size grows by 25% over the previous. This works well when item sizes are roughly uniform or grow gradually. If your application stores items larger than the largest configured chunk, memcached either routes them to the nearest larger class or, depending on -f, rejects or evicts them.
How a slab class works
Each slab class is independent. Items placed in a class stay within that class's LRU list. When a class fills, the least recently used item in that class is evicted to make room for new items of the same size range. Because eviction is per-class, the cache does not scan globally; behavior is predictable as long as the size distribution stays within a few slab classes. The binary protocol's CAS token namespace is per-process and respects the slab class, but the core eviction logic has not changed since memcached 1.2.x.
Mapping item size to a slab class
- With
-f 1.25, chunk sizes grow by 25% each step, creating fewer classes but larger jumps between them. Suitable when item sizes are clustered or grow gradually. - With
-f 1.0, chunk sizes grow by 1% (effectively), creating many more classes. This reduces internal fragmentation per class but increases metadata overhead and can push the number of active classes higher, especially on 64-bit builds.
Operational checks you can run today
You can inspect the current slab layout without restarting the server. Run:
echo 'stats' | nc localhost 11211The response includes a slab classes section listing each class's chunk size, number of chunks, and current item count. Fields such as items and citems show how many items are alive and cached. To watch eviction in action, monitor the items:evicted statistic across classes after injecting test items of varying sizes. A practical check:
- Start memcached with a known memory limit, e.g.,
memcached -m 64 -f 1.25. - Use
addto insert items across a size range:add small 0 3600 4 helloandadd large 0 3600 50000 data.json. - Re-run the
statscommand and note items:evicted and per-class item counts. - If items:evicted rises sharply when you add items near the largest chunk size, your -f factor may be too small for the size spread.
Required permissions: network access to port 11211 (no root needed unless binding to a low port). Meaningful placeholders: small, large, 4, 50000, 3600. Expected checks: per-class items count, global items:evicted, and whether the add command succeeds or returns an error. Relevant risks: mis-interpreting items:evicted as a global counter when it is per-class, or assuming a single maxitem limit applies uniformly when it is derived from the largest slab class chunk size.
Failure modes and conditions that shift the design
- Per-slab-class LRU: Using many item sizes causes frequent class switches. Each switch adds metadata overhead; if you anticipate a broad size distribution, consider partitioning keys by size or pre-provisioning enough classes via a larger -f.
- Incorrect maxitem: If the largest chunk in the highest slab class is 1 MB and you set maxitem higher, memcached may still accept the item but place it in a overflow path that triggers immediate eviction or returns an error. Verify by checking that the largest chunk size (from stats slab classes) covers your expected maximum item.
- Concurrency and expiration delay: Under high concurrency, the single-threaded event loop may delay processing of expired items. Items can remain accessible slightly beyond their intended TTL. This is not an eviction issue but a visibility issue; monitor items:age to see how long items stay after TTL.
- Version stability: Core slab allocation and LRU eviction logic have been unchanged since memcached 1.2.x. Newer versions added binary protocol CAS but kept the fundamental eviction behavior. Running the same workload on 1.4.x and 1.6.x will show identical slab allocation output, confirming that design choices are version-stable.
Concrete sizing example
Suppose you run a cache that stores user profiles (approx 2 KB) and occasional audit logs (approx 300 KB). With the default -f 1.25, the slab classes jump by 25% each step. Starting from 88 bytes, classes progress toward 1 MB. A 300 KB item lands in a class whose chunk size is approx 256 KB-512 KB range, while a 2 KB item lands much earlier. If you instead set -f 1.0, you'd have many more classes (each growing by 1%), reducing per-class waste but increasing the number of active slab classes and the metadata cost. Empirically, for this mix, -f 1.25 keeps the number of active classes under 30, which is manageable, while -f 1.0 could push toward 80+ classes on a 64-bit build, increasing lock contention during class initialization.
To validate: start memcached with memcached -m 128 -f 1.25, inject 2 KB and 300 KB items via add, then run echo 'stats' | nc localhost 11211. Observe two slab classes with non-zero item counts and a low items:evicted rate. If you switch to -f 1.0 and repeat, you'll see more classes reported and potentially higher overhead, but eviction behavior per class remains identical because the LRU logic is unchanged.
Verification and limitations
To verify that your sizing meets expectations, combine stats output with targeted item injection. After inserting items of known sizes, check that the corresponding slab class shows the expected items increment and that items:evicted stays near zero if total items fit within the configured memory and chunk boundaries. If items:evicted climbs without a proportional increase in total items, revisit the -f factor or consider redistributing key sizes.
Limitations remain: LRU eviction is per-slab-class, so a few very large items can fill their class while smaller classes stay under-utilized, wasting memory. The single-threaded event loop means expiration processing can lag under load, so items:age may temporarily exceed the TTL. These are well-documented behaviors, not bugs, and are mitigated by over-provisioning memory or by designing your application to keep item sizes within a narrow range.
Version note
The core slab allocation and LRU eviction logic have been unchanged since memcached 1.2.x. Newer versions added the binary protocol's CAS operation but kept the fundamental eviction behavior. If you run the same workload on memcached 1.4.x and 1.6.x, you will see identical slab class output, confirming that the design choices described here are stable across the maintained release series.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.