前言
SGI特殊空间配置器,std::alloc
构造和析构基本工具:construct()与destroy()
std::alloc空间的配置与释放
一级配置器:
二级配置器:
空间配置函数allocate()
空间释放函数deallocate()
重新填充free lists:refill()函数
内存池
内存基本处理工具
①uninitialized_copy
②uninitialized_fill
③uninitialized_fill_n
SGI STL 的每一个容器都已经指定其缺省的空间配置器为alloc,虽然SGI也定义有一个符合部分标准的配置器allocator,但SGI不使用它,主要原因是效率不佳,它只是基层内存配置/释放(new和delete)的一层薄包装,无任何效率强化
一般来说,new一个对象包含2段操作:
①调用new配置内存
②调用构造函数构造对象内容
同理,delete也包含2段操作,先析构,后释放内存
为精密分工,STL allocator决定将两段操作分开:
内存配置由alloc:allocate( )负责,内存释放由alloc:deallocate( )负责
对象构造由construct( )负责,对象析构由destroy( )负责
construct()里实际调用的是new操作,new一个对象
destroy()有2个版本:
第一版本接受一个指针,将指针所指析构掉
第二版本接受2个迭代器,将范围内的所有对象析构掉,析构前,会先判断析构函数是否无关痛痒(暂时理解为没有内存使用),如果是,则什么都不做就结束,如果不是,则遍历每一个对象调用第一个版本的destroy( )函数
双层级配置器:
配置区块大于128bytes时,调用一级配置器,直接使用malloc和free配置空间
配置区块小于128bytes时,为减小额外开销,会采用复杂的memory pool(内存池)整理方式配置空间
二级配置器细节:
二级配置器实际是一个大内存,维护了一个链表,链表有16个free-lists,各自管理的大小分别为8,16,24…128bytes,有内存需求时,直接从链表中拨出去,释还内存时,则由配置器回收到free-lists中
为什么这样做?
因为无论向系统索要多大的内存,系统都需要额外的用以记录内存的大小,会显得很浪费,此外频繁索要的话,配置也会产生额外的负担,而且还会造成内存碎片,所以内存池是一个明智的选择
为方便管理,SGI二级配置器会主动将内存需求量上调至8的倍数
为了维护链表,每个节点都需要额外的指针,指向下一个结点,这可能会造成浪费,但SGI将指针和结点声明为一个union,达到一物二用的效果,所以不会造成浪费
alloc并不接受任何template型别的参数,它的一二级配置器判断,是通过判断一个_USE_MALLOC常量是否被定义选择的
无论alloc被定义为一级还是二级配置器,SGI都把它封装成一个接口simple_alloc,容器全部都是使用这个接口
_default_alloc_template拥有配置器的标准接口函数allocate()
此函数首先判断区块大小,大于128bytes则调用一级配置器,小于则检查对应的free list,若有可用区块,则分配出去,若没有可用区块,则将区块大小上调至8的倍数,再调用refill()函数,为free list重新填充空间
同理,先判断区块大小,大于128bytes则直接调用一级配置器,小于则回收到在free list中的对应位置中
当allocate()发现free list中没有可用区块时,会调用refill函数,refill函数会调用chunk_alloc()函数,向内存池取20个结点(取得的结点数可能少于20),当取得的结点数为1个时,则直接配置给客户,free list不变,当取得的结点数大于1个时,则将第一个结点分配给客户,剩下的加入到free list对应的位置中
chunk_alloc()函数以end_free - start_free来判断内存池的水量
若水量充足,足以供应1个以上的区块,则拨出这些空间出去
若水量不足,一个空间都分配不了,则运用malloc从heap中配置内存(新内存的大小为需求量的两倍再加上一个随配置次数增大的附加量)
以下例子非常精彩
图2-7即为下面的图
当system heap空间都不够了,malloc()行动失败,则chunk_alloc()会寻找其它未用过的且够大的区块,找到了就交出,找不到了就调用一级配置器,尽管一级配置器也是malloc()配置的,但它有out-of-memory处理机制,有机会释放其它内存拿来此处使用
若输出目的[result, result+(last-first))之间的每一个迭代器都指向未初始化的区域,则uninitialized_copy()会调用copy constructor复制输入来源[ first,last )中的每一个对象存到输出范围中
uninitialized_copy要么构造全部,要么不构造
若[ first,last )范围内每个迭代器都指向未初始化的区域,则uninitialized_fill() 会在该范围内产生 x (上式第三参数)的复制品
如果 [first, first+n) 范围内的每一个迭代器都指向未初始化的内存,那么
uninitialized_fill_n() 会呼叫 copy constructor,在该范围内产生 x (上式
第三参数)的复制品