空间配置器 allocator
在STL中,所有的元素都是存放在容器中,容器需要配置空间来储存这些数值,因此需要用到空间配置器。
概览
- SGI的空间配置器
- SGI标准的空间配置器是allocator, 只是对基层内存配置/释放行为(对运算符new/delete)进行了一层薄薄的封装,没有考虑到效率上的优化。
- SGI特殊的空间配置器是alloc,
SGI以malloc()和free()完成内存的配置和释放。考虑到小型区块可能造成内存破碎问题,SGI设计了双层级配置器。第一层直接用malloc和free,第二级配置器视情况采用不同策略。如果配置区别大于128bytes,选用第一级配置器;当小于128bytes时,采用复杂的memory pool的整理方式。是否同时开放第二级配置器,取决于__USE_MALLOC是否被定义。
精细分工
STL Allocator的精细分工
stl_construct.h
内存配置:allocate()
;
内存释放前进行对象的析构:destroy()
,调用析构函数;
stl_alloc.h
内存配置后进行对象的构造:construct()
,placement new调用构造函数;
内存释放:deallocate()
;
stl_uninitialized.h
定义一些全域函式,用来填充(fill)或者复制(copy)大块的内存内容。stl_construct.h
construct()接收一个指针和一个初始值value,用途是将初值设定到指针所指的空间上
destroy有两个版本,版本1接受一个指针,准备将该指针所指之物析构掉;版本2是接收first和last两个迭代器,将[first,last)范围内所有对象析构掉std::alloc(设置配置器)
SGI用malloc和free这2个C函数代替完成内存的申请和释放。考虑到内存碎片问题,SGI设计了两级配置器,第一级直接使用malloc和free,包括了内存不足处理机制,需要调用者自己提供。第二级判断配置区超过128字节,调用第一级配置器。小于128字节,采用内存池管理
双层级配置器
第一级配置器
__malloc_alloc_template
SGI第一级配置器的allocate()和realloc()使用malloc与free来配置和释放内存。调用不成功后,会调用oom_malloc()和oom_realloc()。后两个函数都有内循环,不断调用“内存不足处理例程”,期待在某次调用后获得足够内存而圆满完成任务。设定“内存不足处理例程”是客端的责任。第二级配置器
__default_alloc_template
当区块小于128byte,交由第二级配置器处理。用内存池(memory pool)来管理。也叫次层配置(sub-allocation)实现方式
- 每次配置一大块内存,并维护对应的自由链表
- 如果下次有相同大小的内存需求,就从free-list中拨出
- 如果客端释放小额区块,就由配置器回收到free-list中
- SGI第二级配置器会主动将任何小额区块的内存需求量上调至8的倍数。它维护16个free-lists,各自管理大小分别为8,16,24,32,…,128byte的小额区块。
- 节点结构
//利用union的性质使得链表既是指针又是实际区块 union obj { union obj * free_list_link; char client_data[1]; //柔性数组 };
内存池
chunk_alloc的工作:从内存池中取空间给free-list使用。if(内存池水量足够) 直接调出20个区块给free list else if(内存池水量还足够提供至少1个区块) 调出实际能够供应的区块 else 利用malloc()向堆heap中配置内存,为内存池注入源头活水以应付需求。一般申请为需求量的两倍,再加上随着配置次数增加而越来越大的附加量 if(heap的空间也不够) malloc()失败,调用第一级配置器中的out of memory处理机制,或许有机会释放其内存拿来此处使用。如果可以就成功,否则发出bad_alloc异常。
相关问题
STL里面空间分配是怎么样的?
STL中用allocator类来实现空间分配,有一级配置器二级配置器,根据一个环境组态来确定使用哪一级的配置器。SGI STL将alloc设置为第二级配置器。如果用让你写一个STL的空间配置器,这个配置器需要经常分配大量的小内存,但大量的分配小内存会造成内存碎片,你会怎么解决这个问题?那如果用你实现的配置器分配的空间是怎么释放的?
参考:可以说一些二级配置器是怎么实现的,可以参照STL特有的空间配置器alloc的设计- 针对于小内存的问题,可以像第二级配置器一样来实现,用内存池来管理。每次配置一大块内存,然后维护16个自由链表free-lists。每个链表会维护若干个小额区块,每个小额区块的大小是8的倍数。
- 如果自由链表里的小额区块不够了,利用chunk_alloc(size,num),从内存池中取空间给free-list使用(参考上面的内存池内容)
- 如果客端申请需求量小于128byte,会将申请的内存需求量上调至8的倍数,然后再查找对应的自由链表中的小额区块。如果大于128byte,就用第一级配置器的allocate()函数,直接用malloc来配置内存。
- 释放内存时,如果释放量小于128byte,配置器会将它归还到对应的自由链表中。如果大于128byte,就用第一级配置器的deallocate()函数,直接用free来释放内存。