Slab 算法浅析
在内存管理中,伙伴算法以 Page 为最小分配单元,适合中大块内存分配。但在频繁分配小内存的场景下,如果每次都按 Page(通常为 4KB 或更大)来分配,就会产生明显的浪费。Slab 算法正是为此而生——它在伙伴算法的基础上,针对小对象分配进行了专门优化,引入了**内存池(Memory Pool)**的思想,从而有效降低内部碎片率。
核心思想
Slab 算法将一大块连续内存预先切分成大小一致的小块(称为 slot),每个 slot 只存放相同类型的对象。这样:
- 小对象分配直接从 slot 获取,避免频繁向系统申请内存。
- 对象释放后并不归还给操作系统,而是留在缓存中,供下次快速复用。
- 支持对象初始化缓存,避免重复构造的性能损耗。
这种“按类型缓存对象”的机制,使 Slab 在高频小内存分配中表现极为高效。Linux 内核正是大量采用了 Slab,用于管理内核对象的生命周期。
数据结构与分配流程
Slab 算法维护多级结构:
-
cache_chain
顶层链表,管理多个kmem_cache
实例。 -
kmem_cache
每个kmem_cache
管理一类固定大小对象的内存池,内部将内存切分成 slot,并用位图(bitmap)记录 slot 的使用状态。 -
Slab 链表
每个kmem_cache
下维护三条链表:- slab_full:已分配满的 Slab。
- slab_partial:部分被使用的 Slab。
- slabs_empty:完全空闲的 Slab。
分配过程如下:
- 当需要分配对象时,优先从
slab_partial
获取可用 slot;若没有,则从slabs_empty
获取新 Slab。 - 当释放对象时,Slab 不会被立刻销毁,而是保留在缓存中以便下次复用。
- Slab 会在三个链表间动态流转:
slab_partial → slab_full
(被填满)
slab_full → slab_partial
(有对象被释放)
slab_partial → slabs_empty
(完全空闲)
特点与优势
- 高速分配:内存池预分配 + 对象缓存机制,减少系统调用。
- 低碎片率:固定大小 slot 避免内部碎片,同时无需频繁合并分裂内存块。
- 对象复用:缓存已初始化的对象,降低 CPU 负担。
- 类型隔离:同类对象共享 Slab,简化内存管理逻辑。
总的来说,Slab 算法在性能与内存利用率之间取得了良好平衡,是小对象高频分配场景的理想选择。