2.SGI STL 内存池 空间配置器重要成员解析
本文深入解析了SGISTL二级空间配置器的核心设计原理。文章首先指出服务器开发中海量小对象分配释放的性能瓶颈,并揭示STL容器默认使用内存池的底层机制。重点剖析了二级空间配置器的关键设计:通过8字节对齐、16条自由链表管理≤128字节的小对象;采用union联合体实现零内存开销的空闲节点复用;使用静态变量记录内存池全局状态;通过volatile关键字保证多线程安全。相比一级配置器直接调用mallo
在服务器开发中,海量小对象的频繁分配与释放是性能瓶颈的重灾区(网络协议包、玩家实体、临时对象等)。原生 malloc/free 会带来严重的内存碎片与系统调用开销,而 SGI STL 实现的二级空间配置器,正是通过内存池解决这一问题的工业级方案。
上一篇我们解析了 vector 的内存管理逻辑,得知所有 STL 容器的内存都依赖空间配置器(Allocator)。本篇我们深挖核心:从默认配置器的宏定义,到二级内存池的底层数据结构,逐行拆解源码,彻底搞懂 STL 内存池的根基设计。
一、STL 容器的「默认配置器」宏:__STL_DEFAULT_ALLOCATOR
我们先回到 vector 的模板定义,它的第二个模板参数就是空间配置器,默认值为 __STL_DEFAULT_ALLOCATOR(_Tp):
template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class vector : protected _Vector_base<_Tp, _Alloc>
这是一个宏定义,是 SGI STL 切换「一级配置器」和「二级配置器」的总开关,源码如下:
# ifndef __STL_DEFAULT_ALLOCATOR
# ifdef __STL_USE_STD_ALLOCATORS
# define __STL_DEFAULT_ALLOCATOR(T) allocator< T > // 标准分配器(包装层)
# else
# define __STL_DEFAULT_ALLOCATOR(T) alloc // 二级内存池配置器(默认)
# endif
# endif
核心结论
- 默认场景:SGI STL 直接使用
alloc(二级空间配置器 + 内存池); - 标准兼容场景:使用
allocator<T>(C++ 标准兼容的包装类); - 这就是 STL 容器无感使用内存池的底层秘密。
二、包装类 allocator:标准兼容的「薄封装」
很多同学会混淆 allocator 和真正的内存池配置器,其实它只是一层适配层,底层依然调用内存池逻辑:
template <class _Tp>
class allocator {
typedef alloc _Alloc; // 底层真正的执行者:二级配置器 alloc
public:
// 类型定义(标准STL要求)
typedef size_t size_type;
typedef _Tp* pointer;
// ... 其他类型别名
// 分配内存:转发给二级配置器 alloc::allocate
_Tp* allocate(size_type __n, const void* = 0) {
return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n * sizeof(_Tp)))
: 0;
}
// 释放内存:转发给二级配置器 alloc::deallocate
void deallocate(pointer __p, size_type __n)
{ _Alloc::deallocate(__p, __n * sizeof(_Tp)); }
// 定位new:在指定内存构造对象
void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); }
// 析构对象:不释放内存
void destroy(pointer __p) { __p->~_Tp(); }
};
allocator 不做任何内存管理,纯转发;真正实现内存池、分配内存的是 alloc;它的作用仅仅是兼容 C++ 标准容器的接口规范。
三、一级配置器 vs 二级配置器:核心分工
SGI STL 提供了两套内存管理方案,各司其职:
1. 一级空间配置器
// 直接封装 malloc / free,无内存池优化
template<int __inst>
class _malloc_alloc_template;
- 适用场景:大于 128 字节的大内存分配;
- 实现原理:直接调用系统
malloc/free; - 特点:简单粗暴,无碎片优化。
2. 二级空间配置器(默认,核心)
// 基于内存池 + 自由链表,专门优化小对象
template <bool threads, int inst>
class __default_alloc_template;
- 适用场景:≤ 128 字节的小对象(游戏服务器核心场景);
- 实现原理:内存池预分配 + 自由链表复用;
- 特点:零系统调用开销、无内存碎片、极高性能。
而我们常用的 alloc,就是二级配置器的别名:
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
__NODE_ALLOCATOR_THREADS:控制是否线程安全(C++ 标准 STL 不保证线程安全)。
四、二级空间配置器:内存池核心常量设计

二级配置器的所有规则,都由 3 个枚举常量定义,这是内存池的设计基石:
enum {_ALIGN = 8}; // 8 字节对齐(内存对齐规则)
enum {_MAX_BYTES = 128}; // 小对象上限:128 字节
enum {_NFREELISTS = 16}; // 自由链表数量:16 个
- _ALIGN = 8所有小对象都按 8 字节向上对齐(8/16/24...128),保证 CPU 高效访问,避免未对齐访问导致的性能损耗。
- _MAX_BYTES = 128超过 128 字节的对象,直接交给一级配置器(malloc),小对象才走内存池。
- _NFREELISTS = 1616 条自由链表,分别管理:8、16、24 ... 128 字节的内存块,精准匹配不同大小的小对象。
五、灵魂设计:union _Obj 空闲节点「零内存开销」复用
这是 SGI 内存池最经典的设计!不额外占用内存存储链表指针,完美利用空闲块本身空间:
// 内存块节点联合体
union _Obj {
_Obj* _M_free_list_link; // 空闲时:存下一个空闲块的指针
char _M_client_data[1]; // 使用时:给用户存储数据(占位符)
};
为什么用 union(联合体)?
- 同一时间只能使用一个成员,内存共享;
- 空闲状态:用
_M_free_list_link串起自由链表; - 使用状态:整块内存给用户,指针字段失效,无任何内存浪费;
- 这是内存池高性能、低开销的关键!
六、核心管理结构:自由链表数组 _S_free_list
二级配置器用指针数组管理 16 条自由链表:
// 静态自由链表数组:16 个链表头指针
static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];
关键细节
- 静态成员变量属于类,所有实例共享,全局唯一的内存池链表。
__STL_VOLATILE关键字禁止编译器优化缓存,保证多线程环境下,线程读取到最新的链表数据(游戏服务器多线程必备)。- 默认初始化所有链表头指针初始化为
0(C++11 后推荐nullptr),代表链表为空。
七、内存池全局状态变量
三个静态变量,记录整个内存池的「家底」:
// 内存池起始地址
static char* _S_start_free;
// 内存池结束地址
static char* _S_end_free;
// 内存池总分配大小
static size_t _S_heap_size;
// 类外初始化(全为 0)
template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_start_free = 0;
template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_end_free = 0;
template <bool __threads, int __inst>
size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0;
作用一览
| 变量名 | 作用 |
|---|---|
_S_start_free |
内存池剩余空间的起始地址 |
_S_end_free |
内存池剩余空间的结束地址 |
_S_heap_size |
累计向操作系统申请的总堆内存大小 |
这三个变量是内存池扩容、分配、回收的核心依据。
本章总结
本篇我们彻底拆解了 SGI STL 二级空间配置器的重要成员,这是理解内存池的前置必备知识:
- STL 容器默认使用二级空间配置器(内存池),由宏定义控制;
allocator是标准适配层,真正干活的是alloc内存池;- 二级配置器管理 ≤128 字节小对象,按 8 字节对齐,16 条自由链表;
union实现空闲节点零开销链表,volatile保证多线程安全;- 全局变量记录内存池状态,支撑后续内存分配逻辑。
(原笔记:GameServer-Learning/00-Notes/C++/SGI_MemoryPool at main · maomianbaobumoyu/GameServer-Learning)
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)