ngx_slab_t是Nginx专为高频小内存分配设计的两级slab分配器,采用页(4096B)与槽(slot)分层管理,结合位图实现O(1)分配/释放;支持预设slot尺寸(8B–2KB),小于等于512B时用位图精确追踪页内空闲状态,无锁、低碎片、高局部性。

ngx_slab_t 是 Nginx 内存管理中极为关键的一环,专为高频、小块、生命周期相近的内存分配场景设计(如连接结构体、HTTP 头部缓存、共享内存中的会话数据等)。它不是通用 malloc 替代品,而是一个紧耦合、零锁(多进程下靠进程隔离)、低碎片、高局部性的 slab 分配器。理解其算法细节,对调优 Nginx 性能、排查共享内存异常、甚至二次开发模块至关重要。
核心设计思想:两级分层 + 位图管理
ngx_slab 并非直接管理物理页,而是将大块连续内存(通常来自 shm 或主 pool)预先划分为固定大小的“页”(page,Nginx 中 1 page = 4096 字节),再在每页内按需切分成更小的“slot”(槽)。整个过程分两层:
-
页级管理:用一个
ngx_slab_page_t数组记录每一页的状态(free / used / partial),数组索引即页号;所有页组成一个双向链表(slots按 slot 大小分链,pages链表管整页)。 -
槽级管理:每个 slot 大小(如 8B、16B、32B…512B、1KB、2KB 等)对应一个空闲链表头(
slots[n]),链表节点是该大小的空闲内存块首地址;小于等于 512B 的 slot 还额外使用位图(bitmap)精确标记页内每个 slot 是否已分配,实现 O(1) 分配/释放判断。
slot 大小分级与位图计算逻辑
Nginx 不支持任意 size 分配,而是预定义一组离散的 slot 尺寸(通过 ngx_slab_sizes 数组),最大支持到 2KB。关键点在于位图如何适配不同 slot 大小:
- 一页 = 4096 字节;若 slot 大小为
s字节,则一页最多容纳n = 4096 / s个 slot。 - 位图长度(字节数)为
(n + 7) / 8,每个 bit 对应一个 slot;bit=0 表示空闲,bit=1 表示已用。 - 例如:
s = 16→n = 256→ 位图长 32 字节;s = 128→n = 32→ 位图仅 4 字节。 - 分配时,遍历位图找第一个 0 bit,计算偏移后返回对应地址;释放时,清对应 bit 即可 —— 无指针跳转,cache 友好。
分配流程:从 slot 查找 → 页内定位 → 位图更新
调用 ngx_slab_alloc 时,实际走以下路径:
- 根据请求 size 查找最接近且 ≥ 的预设 slot 大小
s(向上取整到最近 slot)。 - 查
pool->slots[s_index]链表:若有空闲块,直接摘下返回;否则进入页分配。 - 从
pool->pages链表中找一个page->slab == 0(全空)或page->slab (部分空)的页;若无,则尝试合并相邻空页或失败(返回 NULL)。 - 若选中全空页:初始化其位图全 0,设置
page->slab = 0(已用 slot 数),page->next指向自身(表示未加入任何 slot 链表),然后插入到对应slots[s_index]链表头部。 - 在该页位图中找第一个空闲 bit,置 1,计算地址(页起始 + bit_pos × s),返回。
释放与合并:位图清零 + 页状态迁移
ngx_slab_free 不仅清 bit,还决定页是否可回收:
- 先根据指针反推所属页(地址对齐到 4096 边界),再算出页内偏移和 bit 位置,清对应 bit。
- 递减
page->slab(已用 slot 数);若减为 0,说明整页空闲 → 从slots[s_index]链表摘下,插入pool->pages链表头部,并标记page->next = NULL(表示 free page)。 - 若
page->slab由 1 变 0,则该页从 “partial” 状态回到 “free”,可被其他 slot 复用;但 Nginx 不自动跨 slot 合并(比如 16B 页不会拆给 32B 请求),保证确定性。 - 注意:释放不触发立即内存归还 OS,只更新内部状态;整个 slab pool 的内存由创建者(如 shm)统一管理生命周期。
掌握这些机制后,你就能读懂 ngx_slab_stat 输出、解释 shmtx 争用原因、合理设置 ssl_session_cache 大小避免 slab 耗尽,也能在自定义共享内存模块时安全复用这套高效分配逻辑。










