空间配置器

一级空间配置器:只进行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借的是一个连续的区域,回收内存块方便

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值