一级空间配置器:只进行malloc堆空间的申请。
缺点:频繁的用户态到内核态的切换,开销大
二级空间配置器(默认):当申请的内存>128B时,使用malloc申请堆空间;当申请的内存<=128时,使用自由链表管理内存分配
优点:申请小内存时,开销小速度快
自由链表
16个元素的数组:_S_free_list[_NFREELISTS]
数组中每个元素管理一条链表:比如下标为3的数组元素,管理由很多个大小为32B的空闲内存组成的链表。下标为15的数组元素管理由128B组成的链表
内存池:malloc申请的多余内存(没有构成链表的)不进行释放,由内存池管理
链表的形成(以申请32B内存块为例):先申请32B*40大小的内存,然后把内存切割成20个32B内存后,把后面的19个内存块串成链表,第1个内存块分配出去,数组元素指向19个内存块中的第一块 ;内存块用完后再放回链表
每条链表最多挂20个内存块
源码
template <class _Tp> class allocator { //空间配置器 typedef alloc _Alloc; public: _Tp* allocate(size_type __n, const void* = 0) { //申请内存 return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n * sizeof(_Tp))) //allocate()是_Alloc类的静态成员函数 : 0; } void deallocate(pointer __p, size_type __n) //回收内存 { _Alloc::deallocate(__p, __n * sizeof(_Tp)); } void construct(pointer __p, const _Tp& __val) //构建对象 { //在__p指向的的堆空间上构建_Tp对象 new(__p) _Tp(__val); //定位new表达式 } void destroy(pointer __p) { __p->~_Tp(); } //销毁对象,显式调用析构函数 };
-----------------------------------------------------------------------
typedef alloc _Alloc;
# ifdef __USE_MALLOC
//一级空间配置器
typedef malloc_alloc alloc;typedef __malloc_alloc_template<0> malloc_alloc; //__malloc_alloc_template<0>就是_Alloc
else
//二级空间配置器(默认用这个)
//__default_alloc_template<__NODE_ALLOCATOR_THREADS, 0>就是_Alloc
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
//一级空间配置器 template <int __inst> class __malloc_alloc_template { //就是_Alloc类 public: static void* allocate(size_t __n) //调用这个函数申请内存 { void* __result = malloc(__n); if (0 == __result) __result = _S_oom_malloc(__n);//申请失败 return __result; } static void deallocate(void* __p, size_t /* __n */) { free(__p); } static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz) { void* __result = realloc(__p, __new_sz); if (0 == __result) __result = _S_oom_realloc(__p, __new_sz); return __result; } };
-------------------------------------------------------------
//二级空间配置器 template <bool threads, int inst> class __default_alloc_template { //就是_Alloc类,空间配置器默认使用这个类 ............... } ;
以下是二级空间配置器中自由链表的形成过程(即只讲解allocate()
)
class __default_alloc_template { enum { _ALIGN = 8 }; enum { _MAX_BYTES = 128 }; enum { _NFREELISTS = 16 }; static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS]; static char* _S_start_free; static char* _S_end_free; static size_t _S_heap_size; union _Obj { union _Obj* _M_free_list_link; char _M_client_data[1]; /* The client sees this. */ }; static void* allocate(size_t __n) { ..... } };
//初始化
char* _S_start_free = 0;
char*_S_end_free = 0;
size_t _S_heap_size = 0;
static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS] //16个元素的数组
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };static void* allocate(size_t __n) //内存申请 { void* __ret = 0; if (__n > (size_t) _MAX_BYTES) { __ret = malloc_alloc::allocate(__n); } else { _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__n); _Obj* __RESTRICT __result = *__my_free_list; if (__result == 0) __ret = _S_refill(_S_round_up(__n)); else { *__my_free_list = __result -> _M_free_list_link; __ret = __result; } } return __ret; };
1、首先申请32个字节:链表中的内存块是申请堆空间后获得的
_Obj* __STL_VOLATILE* __my_free_list
= _S_free_list + _S_freelist_index(__n); // _S_free_list是数组的首地址//__bytes=32,返回管理32B的数组元素的下标:3 static size_t _S_freelist_index(size_t __bytes) { return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1); } (32 + 8 - 1)/8 - 1 39/8 - 1 = 3
__result = *__my_free_list;// *__my_free_list是对应的数组元素即__STL_VOLATILE _S_free_list[3]=0。__result=0
__ret = _S_refill(_S_round_up(__n)); //返回分配的32B内存块首地址,__ret指向分配的32B内存块
//当__bytes是25~32时,向上取整为32,返回32 static size_t _S_round_up(size_t __bytes) //__bytes = 32 { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }
template <bool __threads, int __inst>
void* _S_refill(size_t __n)//__n = 32
{
int __nobjs = 20;
char* __chunk = _S_chunk_alloc(__n, __nobjs);//32, 20
_Obj* __STL_VOLATILE* __my_free_list;
_Obj* __result;
_Obj* __current_obj;
_Obj* __next_obj;
int __i;
__my_free_list = _S_free_list + _S_freelist_index(32);//__my_free_list指向管理32B的数组元素(数组下标是3)
__result = (_Obj*)__chunk;
*__my_free_list = __next_obj = (_Obj*)(__chunk + __n);//__n=32
for (__i = 1; ; __i++)
{
__current_obj = __next_obj;
__next_obj = (_Obj*)((char*)__next_obj + __n);
if (__nobjs - 1 == __i) {
__current_obj -> _M_free_list_link = 0;
break;
} else {
__current_obj -> _M_free_list_link = __next_obj;
}
}
return(__result); //被分配出去的32B内存块
}
template <bool __threads, int __inst> char*_S_chunk_alloc(size_t __size, int& __nobjs)//32, 20 { char* __result; size_t __total_bytes = __size * __nobjs;// = 32 * 20 = 640 size_t __bytes_left = _S_end_free - _S_start_free;//= 0-0=0 if(){} if(){} else{ size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4);//_S_heap_size=0 //= 2 * 640 + 0=1280 _S_start_free = (char*)malloc(__bytes_to_get);//指向分配的1280 _S_heap_size += __bytes_to_get; //= 1280 _S_end_free = _S_start_free + __bytes_to_get;//=_S_start_free+1280 //递归调用 char* __result; size_t __total_bytes = __size * __nobjs;// = 32 * 20 = 640 size_t __bytes_left = _S_end_free - _S_start_free;// = 1280 if (__bytes_left >= __total_bytes) {//1280>=640 __result = _S_start_free;//申请的1280B的首地址 _S_start_free += __total_bytes;//偏移640B return(__result); } } }
2、然后申请64个字节:链表中的内存块是从内存池获得的(内存池中的内存是申请32B内存时剩余的)
_Obj* __STL_VOLATILE* __my_free_list
= _S_free_list + _S_freelist_index(__n); //_S_freelist_index(64)返回数组下标:7__result = *__my_free_list;//__STL_VOLATILE _S_free_list[7]=0
__ret = _S_refill(_S_round_up(__n));//_S_round_up(64)返回64;_S_refill(64)
void* _S_refill(size_t __n)//__n = 64 { int __nobjs = 20; char* __chunk = _S_chunk_alloc(__n, __nobjs);//64, __nobjs:20-》10 _Obj* __STL_VOLATILE* __my_free_list; _Obj* __result; _Obj* __current_obj; _Obj* __next_obj; int __i; __my_free_list = _S_free_list + _S_freelist_index(64);//__my_free_list指向管理64B内存块的数组元素 //接下来的操作和32B内存块形成链表一样 //最后返回__chunk(__chunk指向分配出去的64B内存块) }
char*_S_chunk_alloc(size_t __size, int& __nobjs)//64, 20 { char* __result; size_t __total_bytes = __size * __nobjs;// = 64 * 20 = 1280 size_t __bytes_left = _S_end_free - _S_start_free;//=1280-640=640 else if (__bytes_left >= __size) {//640>=64 __nobjs = (int)(__bytes_left/__size);//=10 __total_bytes = __size * __nobjs;//=64*10=640 __result = _S_start_free;//第一次申请的1280B剩余的640B的首地址 _S_start_free += __total_bytes;//偏移640B return(__result); } }
3、申请96个字节
4、申请72字节,此时内存池耗尽,堆空间没有大于72字节的连续空间(即无法申请堆空间,也无法从内存池获得内存构成链表)
_Obj* __STL_VOLATILE* __my_free_list= _S_free_list + _S_freelist_index(__n);//_S_freelist_index(72)返回数组下标:8
__result = *__my_free_list;//__STL_VOLATILE _S_free_list[8]=0
__ret = _S_refill(_S_round_up(__n));//_S_round_up(72)返回72;_S_refill(8)
void* _S_refill(size_t __n)//__n = 72 { int __nobjs = 20; char* __chunk = _S_chunk_alloc(__n, __nobjs);//__nobjs:20->1 _Obj* __STL_VOLATILE* __my_free_list; _Obj* __result; _Obj* __current_obj; _Obj* __next_obj; int __i; if (1 == __nobjs) return(__chunk);//__chunk指向72B的内存块 }
char* _S_chunk_alloc(size_t __size, int& __nobjs)//72 20 { char* __result; size_t __total_bytes = __size * __nobjs; //72 * 20 = 1440 size_t __bytes_left = _S_end_free - _S_start_free;// = 0 if(){} else if(){} else{ size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4); _S_start_free = (char*)malloc(__bytes_to_get);//堆空间不够申请失败 if (0 == _S_start_free) {//堆空间申请失败 size_t __i; _Obj** __my_free_list; _Obj* __p; //搜寻比72B大并且已经构成链表的内存块 //72 80 88 96 for (__i = __size;//72 __i <= (size_t) _MAX_BYTES;// _MAX_BYTES = 128 __i += (size_t) _ALIGN) //_ALIGN = 8 { __my_free_list = _S_free_list + _S_freelist_index(__i); __p = *__my_free_list; if (0 != __p) //判断数组元素是否为0,不为0说明该数组元素指向链表 { *__my_free_list = __p -> _M_free_list_link;//数组元素指向下一个内存块 _S_start_free = (char*)__p; _S_end_free = _S_start_free + __i; //__i=96 //递归操作 return(_S_chunk_alloc(__size, __nobjs)); } } } } }
return(_S_chunk_alloc(__size, __nobjs));
char*_S_chunk_alloc(size_t __size, int& __nobjs)//64, 20 { char* __result; size_t __total_bytes = __size * __nobjs;// = 72 * 20 = 1440 size_t __bytes_left = _S_end_free - _S_start_free;//=96 else if (__bytes_left >= __size) {//96>=72 __nobjs = (int)(__bytes_left/__size);//=1 __total_bytes = __size * __nobjs;//=72*1=72 __result = _S_start_free; _S_start_free += __total_bytes; return(__result); } }
找比72B大的96B借,而不找比72B小的64B去借的原因:
找64B时,需要借两个内存块,而这两个内存块不一定是连续的(因为中间可能有一些内存块已经分配出去了),所以回收内存块的时候由于不连续不容易拼接回64B的链表。
而找96B借的是一个连续的区域,回收内存块方便