剖析SGI STL空间配置器
在我们使用STL容器的时候,模板最后一个参数会有一个默认的allocator
作为容器模板的参数,这个参数就是STL的空间配置器。容器的空间配置器见这篇文章:容器空间配置器的重要性
STL容器空间配置器实际上就是拆分了new关键字的两个步骤:内存申请和构造对象;和delete的内存释放和对象析构。其底层的内存管理调用的都是malloc和free。
C++STLvector容器的构造函数:
有一个默认的模板参数,我们使用的时候是不需要传的,他有四个重要的函数:
allocate:负责给容器开辟内存空间,实际上调用就是malloc
deallocate:负责释放容器开辟的内存,实际上调用的就是free
construct:负责给容器构造一个对象,实际上就是用定位new实现
destory:负责析构容器的一个对象,实际上就是调用对象的析构函数,~p->_Ty();
如果我们的应用场景中涉及小块内存的开辟和释放,短时间内调用的次数特别多,而malloc和free的底层实现是比较复杂的。如果我们频繁地对小块内存分配和释放一直使用malloc和free,不仅会拖慢我们地程序运行效率,还会产生很多的内存碎片。
SGI STL空间配置器就是为了解决这个问题,它分为一级空间配置器和二级空间配置器,其中一级空间配置器用malloc和free来管理内存,和C++STL标准模板库的空间配置器是一样的。而二级空间配置器采用了基于freelist自由链表的形式,以内存池机制来实现小块内存的管理。
STL是C++的标准模板库,而SGI STL是一个第三方厂商开发的STL,包含了STL设计的组件。
查看SGI STLvector容器的定义:
可以看到也给了一个默认的allocater,查看这个宏我们可以看到,我们可以通过定义宏__STL_USE_STD_ALLOCATORS
来控制用不用SGI STL提供的alloc,查看vector的push_back操作:
可以看到其容器元素的构造和STL的动作是一致的,通过定位new来实现的。同理,_Destory函数调用的就是对象的析构函数。
SGI STL和C++STL空间配置器的对象构造和对象的析构都一致,不同的主要是在内存开辟和释放。
查看SGI STL的空间配置器的空间配置器alloc
可以完整的看到它所包含的参数,下面一一对这些参数进行剖析。
- _ALIGN:翻译过来就是对齐,小块内存的分配默认是以8字节对齐的,分配内存是系统位数的整数倍可以加快内存的分配速度
- _MAX_BYTES:小大块内存的分界,默认最大的小块内存
- _NFREELISTS:自由链表的长度,等于_MAX_BYTES/_ALIGN
- _S_round_up函数:提升小块内存的大小提升到8的倍数,比如申请(1~7)字节的空间,会提升到8字节,然后分配8字节出去,方便内存的管理和分配速度,返回实际申请的内存大小,
- union _Obj:链表结构的联合体,用来记录未分配的内存块
- _S_free_list:自由链表数组,存储不同8~128内存块的头节点,长度为_NFREELISTS,也就是16
- _S_freelist_index函数:获取对应大小内存块在自由链表的下标并返回
除了这些参数,类外还定义了一些函数来维护内存池
- _S_refill函数:分配一个chunk块,大小为__n,传参时需要先调用_S_round_up函数提升到8的倍数
- _S_chunk_alloc函数:内存池的内存来源,chunk块不足时从这里填充,备用池不足时malloc申请,malloc失败还可以调用预先设置的回调函数释放内存
- _S_start_free:维护备用内存池的起始地址,初始化为空
- _S_end_free:维护备用内存池的末尾地址,初始化为空
- _S_heap_size:记录上次填补备用池的大小,初始化为0
其结构初始化如下图所示: