情况一:内存池剩余空间的大小不满足需要分配的内存空间
假设调用为 _S_chunk_alloc(8, 20)
, 即希望分配8个20B的内存小块构成的chunk
块
代码如下
template <bool __threads, int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size,
int& __nobjs)
{
char* __result;
size_t __total_bytes = __size * __nobjs; // 需要的内存大小 20 * 8 = 160
size_t __bytes_left = _S_end_free - _S_start_free; // 剩余可用空间大小
// 剩余可用空间大小 > 需要的内存大小 ,直接把内存给用户
if (__bytes_left >= __total_bytes) {
__result = _S_start_free;
_S_start_free += __total_bytes;
return(__result);
}
else if (__bytes_left >= __size) {
// 比如需要16字节的内存块,可以从8字节的内存块链表的备用部分取用
// 即16字节的内存块链表起始地址指向8字节的内存块链表中的后备块
// 剩余可用空间大小 < 需要的内存大小 但是 > 所需要的单个内存块的大小
__nobjs = (int)(__bytes_left/__size);
__total_bytes = __size * __nobjs;
__result = _S_start_free;
_S_start_free += __total_bytes;
return(__result);
}
else { // 需要创建新的chunk块的情况,情况一
//
size_t __bytes_to_get =
2 * __total_bytes + _S_round_up(_S_heap_size >> 4);
// Try to make use of the left-over piece.
if (__bytes_left > 0) { // 内存碎片处理,接到对应free_list的头部
_Obj* __STL_VOLATILE* __my_free_list =
_S_free_list + _S_freelist_index(__bytes_left);
((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
*__my_free_list = (_Obj*)_S_start_free;
}
// 分配内存
_S_start_free = (char*)malloc(__bytes_to_get);
if (0 == _S_start_free) {} // if (0 == _S_start_free)
_S_heap_size += __bytes_to_get;
_S_end_free = _S_start_free + __bytes_to_get;
return(_S_chunk_alloc(__size, __nobjs));
}
}
第一轮进入到else中,创建新的chunk块总共320个字节(_S_heap_size
),如下图(这里指针画错了,是8字节的_Obj*
指向trunk)
然后结尾调用 _S_chunk_alloc(__size, __nobjs)
再次进入函数满足__bytes_left >= __total_bytes
即(320 >= 160),将内存小块分配给用户,_S_start_free
指针向下移动,即(这里指针画错了,是8字节的_Obj*
指向trunk)
假如又调用 _S_chunk_alloc(8, 20)
,则把上图下面20个内存块也给用完了,之后再次调用 _S_chunk_alloc(8, 20)
的时候就会进入else逻辑,重新计算
__bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4)
,结果为60(2 * 20 + 320 >> 4),也就是说要再malloc
60个8B的内存块。
情况2:申请其他字节的内存块时可以复用不同字节的内存小块
如刚刚8B的trunk
块还剩余20个内存小块,然后此时来了一个_S_chunk_alloc(16, 20)
调用,__bytes_left <= __total_bytes
(160 < 320),
但是此时 __bytes_left >= __size
,故可以复用备用的内存块即进入代码的 else if (__bytes_left >= __size)
分支中执行,将其按照16B进行划分,示意图如下
若调用_S_chunk_alloc(128, 1)
,则内存池的情况如下:
此时再调用 _S_chunk_alloc(40, 20)
,但是可以看到8B的trunk
里只剩余32B
的空间,则只能进入else
的逻辑,由于剩余空间大于0,则进行内存碎片的处理:把剩余的空间挂接到合适单位字节的trunk块上,即32B的trunk块上,指针做出了如下的变化
可以看出这种设计方式极大的提高了内存池的利用率,具体代码如下
else { // 需要创建新的chunk块的情况
//
size_t __bytes_to_get =
2 * __total_bytes + _S_round_up(_S_heap_size >> 4);
// Try to make use of the left-over piece.
if (__bytes_left > 0) { // 内存碎片处理,接到对应free_list的头部
_Obj* __STL_VOLATILE* __my_free_list =
_S_free_list + _S_freelist_index(__bytes_left);
((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
*__my_free_list = (_Obj*)_S_start_free;
}
// 分配内存
_S_start_free = (char*)malloc(__bytes_to_get);
if (0 == _S_start_free) {
size_t __i;
_Obj* __STL_VOLATILE* __my_free_list;
_Obj* __p;
// Try to make do with what we have. That can't
// hurt. We do not try smaller requests, since that tends
// to result in disaster on multi-process machines.
for (__i = __size;
__i <= (size_t) _MAX_BYTES;
__i += (size_t) _ALIGN) {
__my_free_list = _S_free_list + _S_freelist_index(__i);
__p = *__my_free_list;
if (0 != __p) {
*__my_free_list = __p -> _M_free_list_link;
_S_start_free = (char*)__p;
_S_end_free = _S_start_free + __i;
return(_S_chunk_alloc(__size, __nobjs));
// Any leftover piece will eventually make it to the
// right free list.
}
}
_S_end_free = 0; // In case of exception.
_S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
// This should either throw an
// exception or remedy the situation. Thus we assume it
// succeeded.
}
当分配内存失败时,即如下语句失败时:
_S_start_free = (char*)malloc(__bytes_to_get);
进入if (0 == _S_start_free)
分支,进行借内存去找到第一个有可用内存块的trunk【TODO】
如果借内存也失败,那么就尝试 malloc_alloc::allocate
,也就是会调用__malloc_alloc_template::allocate
,即
static void* allocate(size_t __n)
{
void* __result = malloc(__n);
if (0 == __result) __result = _S_oom_malloc(__n); //
return __result;
}
如果malloc
调用失败,进入_S_oom_malloc
即out of memory
错误时的处理函数,进入该函数后,可以进行错误的处理,如果用户有设置对应处理函数,就会死循环直到内存分配成功。如果用户没有设置相应的处理函数,就会抛出异常终止程序,代码如下
template <int __inst>
void*
__malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
{
// 释放资源的回调,给用户来设置的
void (* __my_malloc_handler)();
void* __result;
for (;;) {
__my_malloc_handler = __malloc_alloc_oom_handler;
if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
(*__my_malloc_handler)();
__result = malloc(__n);
if (__result) return(__result);
}
}