SGI STL(四)——_S_chunk_alloc函数解析

情况一:内存池剩余空间的大小不满足需要分配的内存空间

假设调用为 _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_mallocout 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值