泛读《STL源码剖析》第二章:空间配置器

目录目录目录目录

前言

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)的一层薄包装,无任何效率强化

 

SGI特殊空间配置器,std::alloc

一般来说,new一个对象包含2段操作:

①调用new配置内存

②调用构造函数构造对象内容

同理,delete也包含2段操作,先析构,后释放内存

为精密分工,STL  allocator决定将两段操作分开:

内存配置由alloc:allocate( )负责,内存释放由alloc:deallocate( )负责

对象构造由construct( )负责,对象析构由destroy( )负责

 

构造和析构基本工具:construct()与destroy()

construct()里实际调用的是new操作,new一个对象

destroy()有2个版本:

第一版本接受一个指针,将指针所指析构掉

第二版本接受2个迭代器,将范围内的所有对象析构掉,析构前,会先判断析构函数是否无关痛痒(暂时理解为没有内存使用),如果是,则什么都不做就结束,如果不是,则遍历每一个对象调用第一个版本的destroy( )函数

 

std::alloc空间的配置与释放

双层级配置器:

一级配置器:

配置区块大于128bytes时,调用一级配置器,直接使用malloc和free配置空间

二级配置器:

配置区块小于128bytes时,为减小额外开销,会采用复杂的memory pool(内存池)整理方式配置空间

二级配置器细节:

二级配置器实际是一个大内存,维护了一个链表,链表有16个free-lists,各自管理的大小分别为8,16,24…128bytes,有内存需求时,直接从链表中拨出去,释还内存时,则由配置器回收到free-lists中

泛读《STL源码剖析》第二章:空间配置器_第1张图片图、free list 实现

为什么这样做?

因为无论向系统索要多大的内存,系统都需要额外的用以记录内存的大小,会显得很浪费,此外频繁索要的话,配置也会产生额外的负担,而且还会造成内存碎片,所以内存池是一个明智的选择

泛读《STL源码剖析》第二章:空间配置器_第2张图片图、索求内存模型

为方便管理,SGI二级配置器会主动将内存需求量上调至8的倍数

为了维护链表,每个节点都需要额外的指针,指向下一个结点,这可能会造成浪费,但SGI将指针和结点声明为一个union,达到一物二用的效果,所以不会造成浪费

alloc并不接受任何template型别的参数,它的一二级配置器判断,是通过判断一个_USE_MALLOC常量是否被定义选择的

泛读《STL源码剖析》第二章:空间配置器_第3张图片

无论alloc被定义为一级还是二级配置器,SGI都把它封装成一个接口simple_alloc,容器全部都是使用这个接口

泛读《STL源码剖析》第二章:空间配置器_第4张图片

 

空间配置函数allocate()

_default_alloc_template拥有配置器的标准接口函数allocate()

此函数首先判断区块大小,大于128bytes则调用一级配置器,小于则检查对应的free list,若有可用区块,则分配出去,若没有可用区块,则将区块大小上调至8的倍数,再调用refill()函数,为free list重新填充空间

泛读《STL源码剖析》第二章:空间配置器_第5张图片

泛读《STL源码剖析》第二章:空间配置器_第6张图片

泛读《STL源码剖析》第二章:空间配置器_第7张图片图、free list拨出区块

 

空间释放函数deallocate()

同理,先判断区块大小,大于128bytes则直接调用一级配置器,小于则回收到在free list中的对应位置中

泛读《STL源码剖析》第二章:空间配置器_第8张图片

泛读《STL源码剖析》第二章:空间配置器_第9张图片图、free list回收区块

 

重新填充free lists:refill()函数

当allocate()发现free list中没有可用区块时,会调用refill函数,refill函数会调用chunk_alloc()函数,向内存池取20个结点(取得的结点数可能少于20),当取得的结点数为1个时,则直接配置给客户,free list不变,当取得的结点数大于1个时,则将第一个结点分配给客户,剩下的加入到free list对应的位置中

 

内存池:

chunk_alloc()函数以end_free - start_free来判断内存池的水量

若水量充足,足以供应1个以上的区块,则拨出这些空间出去

若水量不足,一个空间都分配不了,则运用malloc从heap中配置内存(新内存的大小为需求量的两倍再加上一个随配置次数增大的附加量)

以下例子非常精彩

泛读《STL源码剖析》第二章:空间配置器_第10张图片

图2-7即为下面的图

当system heap空间都不够了,malloc()行动失败,则chunk_alloc()会寻找其它未用过的且够大的区块,找到了就交出,找不到了就调用一级配置器,尽管一级配置器也是malloc()配置的,但它有out-of-memory处理机制,有机会释放其它内存拿来此处使用

泛读《STL源码剖析》第二章:空间配置器_第11张图片图、二级配置器的使用

 

内存基本处理工具:

①uninitialized_copy

若输出目的[result, result+(last-first))之间的每一个迭代器都指向未初始化的区域,则uninitialized_copy()会调用copy constructor复制输入来源[ first,last )中的每一个对象存到输出范围中

uninitialized_copy要么构造全部,要么不构造

②uninitialized_fill

若[ first,last )范围内每个迭代器都指向未初始化的区域,则uninitialized_fill() 会在该范围内产生 x (上式第三参数)的复制品

③uninitialized_fill_n

如果 [first, first+n) 范围内的每一个迭代器都指向未初始化的内存,那么
uninitialized_fill_n() 会呼叫 copy constructor,在该范围内产生 x (上式
第三参数)的复制品

 

你可能感兴趣的:(《STL源码剖析》)