STL存在两级空间配置器。其中第一级空间配置器是默认空间配置器。第一级空间配置器用于配置大于128byte的内存,而当申请内存小于等于128bytes时,则求助于第二级空间配置器。
二级空间配置器
如果仅使用第一级空间配置器,那么内存分配过程中存在两个问题:
- 内存碎片(外碎片)
- 频繁分配小内存,不断调用malloc,进而调用底层的系统调用,产生性能问题
注:
内碎片:因为内存对齐/访问效率而产生的,用户需要3字节,实际得到4字节或8字节的问题,其中多出来的字节就是碎片,被浪费了。
外碎片:系统中内存总量足够,但是由于离散分配导致不连续,所以无法分配给用户使用而产生的浪费。比如,系统依次分配了16、8、16、4、8byte,还剩一个8byte未分配,这时要分配一个24byte的空间,系统回收两个16 byte,总的空间剩余40byte, 但是却分配不出来一个24byte。
二级空间配置器是为解决“频繁分配小内存”而产生的一种算法,其实就是为了消除一级空间配置器的外碎片问题。
allocate,refill,chunk_alloc
调用关系:allocate调用refill,refill调用chunk_alloc。
1 | graph LR |
allocate
allocate函数详细过程:
函数原型:void *__default_alloc_template<threads, inst>::allocate(size_t n);
功能:分配指定的n个字节的内存空间。
调用refill时,会将n调整到第一个大于等于n的8的倍数。
流程图
1 | graph TB |
源码
1 | // n must be > 0, 要分配的内存块大小 |
FREELIST_INDEX
1 | enum { |
测试程序
1 | void test1() { |
refill
函数原型:void *__default_alloc_template<threads, inst>::refill(size_t n)
功能:当allocate为客户端分配指定的n(n<=128)个字节的内存,却发现free list空间不足时,就会调用refill(),为free list重新填充空间。
新的空间分配并不由本函数完成,而是调用chunk_alloc来完成。本函数需要完成的是,为chunk_alloc指定需要分配的区块大小(即n个字节)以及区块个数(默认为20)。当chunk_alloc返回分配的内存的首地址时,本函数再根据返回的内存的大小决定是否需要向自由链表中添加内存区块。
流程图
1 | graph TB |
chunk_alloc
函数原型:char *__default_alloc_template<threads, inst>::chunk_alloc(size_t size, int &nobjs)
参数:size为区块大小(8的倍数,从8-128共16种可能的大小);
参数:nobjs为期望分配的区块个数,默认为20。
功能:从内存池中取出空间给free list使用。
流程图
1 | graph TB |
1 | graph TD |
几个疑问
为什么自由链表中分配的内存都需要上调到8字节的倍数?
参考文献
[1]二级空间配置器的必要性:http://www.dongcoder.com/detail-67805.html