STL空间配置器

本文探讨了SGI空间配置器在STL中的角色,如何通过std::alloc优化内存分配,以及其双层级设计(一级malloc/free与二级memorypool)以提高效率。内容包括对象构造析构、内存不足处理和内存池管理。
摘要由CSDN通过智能技术生成

顾名思义,空间配置器为容器分配数据存储空间,它一般隐藏在容器背后,默默工作。

SGI 空间配置器

SGI STL虽然定义了一个符合STL标准的空间配置器std::allocator,但是由于该配置器的效率极低,所以SGI STL使用的缺省空间配置器为std::alloc,它将我们习惯的C++内存配置操作(new关键字)对应的两阶段操作(1、调用::operator new配置内存;2、构造对象内容)区分开来,分别定义于<stl_alloc.h>和<stl_construct.h>两个文件中,由alloc::allocate()和::construct()负责,内存释放操作(delete)做类似处理,由alloc::deallocate()和::destroy()负责。

在这里插入图片描述

对象的构造和析构,construct()和destroy()

在这里插入图片描述

trivial destructor表示对象使用的是系统默认的析构函数,则无需任何操作。non-trivial destructor则表示对象使用了特定定义的析构函数,这就需要对每个对象一一进行析构。至于如何判断是否为trivial destructor,SGI STL提供了__type_traits技法用于萃取型别的特性。

空间的配置和释放,std::alloc

SGI对此的设计哲学如下:

  • 向system heap要求空间
  • 考虑多线程状态
  • 考虑内存不足时的应变措施
  • 考虑过多“小型区块”可能造成的内存碎片问题
双层级配置器

为了更高效地使用空间,SGI设计了双层级配置器,当请求配置的区块超过128bytes时,调用第一级配置器,否则调用第二级配置器。第一级配置器采用malloc()和free()配置和释放内存,而第二级配置器采用复杂的memory pool整理方式。整个设计究竟只开放第一级配置器,还是同时开放第二级配置器,取决于__USE_MALLOC是否被定义,SGI STL未定义该参数,所以SGI STL默认使用第二级配置器。

在这里插入图片描述

第一级配置器

采用malloc()和free()配置和释放内存。当内存不足时,调用客端设计的“内存不足处理例程”,这是模拟的C++ new-handler机制(注意,第一级配置器使用malloc分配内存,所以不能直接使用C++ new-handler机制)。配置器会不断地调用“内存不足处理例程”,直至成功,如果客端没有设定该例程,则丢出bad_alloc异常信息或利用exit(1)硬中止程序。

第二级配置器

第二级配置器维护着16个自由链表,它们各自管理大小分别为8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128 bytes的小额区块,同时,为了方便管理,SGI第二级配置器会主动将任何小额区块的内存需求量上调至8的倍数。

在这里插入图片描述

自由链表使用union结构,极大节省了内存开销,当区块空着的时候节点被视为一个指向相同形式的另一个节点,而当节点中有数据时,则视为指向实际区块。

union obj{
	union obj * free_list_link;
	char client_data[1];
}

使用allocate()和deallocate()配置和释放内存,实现过程如图所示:

(1)allocate()

在这里插入图片描述
(2)deallocate()

在这里插入图片描述

当使用allocate()时发现free list中没有可用区块时,就调用refill(),准备为free list重新填充空间,新空间则取自内存池,该过程由chunk_alloc()完成。缺省取得20个新节点(新区块)。

内存池情况分三种:

(1)内存池剩余空间完全满足需求量,分配所需个数的区块给free list

(2)内存池剩余空间部分满足需求量,只分配相应个数的区块给free list

(3)内存池剩余空间连一个区块的大小都无法提供,将剩余零头分给适当的free list,并从配置堆空间用以补充内存池,如果堆空间也满了,则看看有没有其他free list能用,如果有,就用该区块,如果没有,就山穷水尽,只能再调用一级配置器,看看“内存不足处理例程”有没有什么帮助。

内存基本处理工具

STL定义了5个全局函数,除去之前讨论过的construct()和destroy(),还有uninitialized_copy(),uninitialized_fill()和uninitialized_fill_n(),它们分别对应于高层次函数copy(),fill()和fill_n()。

在这里插入图片描述

POD(Plain Old Data),也就是标量型别(scalar types)或传统的C struct型别。该型别必然拥有trivial ctor/dtor/copy/assignment函数,因此,我们可以对POD型别采用最有效率的初值填写手法,而对non-POD型别采取最保险安全的做法。

针对char*和wchar_t*两种型别,可以采用最具效率的做法memmove(直接移动内存内容)来执行复制行为。

void *memmove(void *str1, const void *str2, size_t n)
  • str1 – 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
  • str2 – 指向要复制的数据源,类型强制转换为 void* 指针。
  • n – 要被复制的字节数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值