G2.9 std_alloc源码剖析

1.alloc类简介

template <bool threads, int inst>
class __default_alloc_template {
private:
// Really we should use static const int x = N
//实际上应该使用 static const int x = N
// instead of enum { x = N }, but few compilers accept the former
//取代enum { x = N },但目前支援性的编译器不多

//第二级分配器

enum {_ALIGN = 8}; //小区块上调边界
enum {_MAX_BYTES = 128}; //小区块的上限
enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN //free-list块数
static size_t
_S_round_up(size_t __bytes)
{ return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }
//若bytes为13,则(13+7)&~(7),即10100 & 11000,得10000即16
__PRIVATE:
union _Obj {  //type definition
union _Obj* _M_free_list_link;
char _M_client_data[1]; /* The client sees this. */
};  //改为struct亦可,embedded pointer

private:
static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];  //16条空链表
static size_t _S_freelist_index(size_t __bytes) {
return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
}

// Returns an object of size __n, and optionally adds to size __n free list.
static void* _S_refill(size_t __n);
// Allocates a chunk for nobjs of size size. nobjs may be reduced
// if it is inconvenient to allocate the requested number.
static char* _S_chunk_alloc(size_t __size, int& __nobjs);

// Chunk allocation state.
static char* _S_start_free; //指向pool的头
static char* _S_end_free;  //指向pool的尾
static size_t _S_heap_size;  //分配累积量
};


2.allocate函数


  /* __n must be > 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**
      _Obj* __RESTRICT __result = *__my_free_list;
      if (__result == 0)  //list为空
        __ret = _S_refill(_S_round_up(__n));  //refill()会填充free list并返回一个(其实就是第一个)区块的起始地址
      else {  //表示list内已有可用区块
        *__my_free_list = __result -> _M_free_list_link;
        __ret = __result;
      }
    }
    return __ret;
  };

3.deallocate函数

  /* __p may not be 0 */
  static void deallocate(void* __p, size_t __n) //p不为0,如果这p并非当初从alloc取得,仍可并入alloc(内)
  {                                                                               //如果p所指大小不是8倍数,甚至会带来灾难
    if (__n > (size_t) _MAX_BYTES)
      malloc_alloc::deallocate(__p, __n);  //改用第一级
    else {
      _Obj* __STL_VOLATILE*  __my_free_list 
          = _S_free_list + _S_freelist_index(__n);  //obj **
      _Obj* __q = (_Obj*)__p;

      // acquire lock
#       ifndef _NOTHREADS
      /*REFERENCED*/
      _Lock __lock_instance;
#       endif /* _NOTHREADS */
      __q -> _M_free_list_link = *__my_free_list;
      *__my_free_list = __q;
      // lock is released here
    }
  }

4.refill函数


/* Returns an object of size __n, and optionally adds to size __n free list.*/
/* We assume that __n is properly aligned.                                */
/* We hold the allocation lock.                                         */
template <bool __threads, int __inst>
void*
__default_alloc_template<__threads, __inst>::_S_refill(size_t __n) //n 已调整至8的倍数
{
    int __nobjs = 20;  //预测取20个区块(但不一定能够)
    char* __chunk = _S_chunk_alloc(__n, __nobjs);  //__nobjs 是 pass-by-reference
    _Obj* __STL_VOLATILE* __my_free_list;  //obj**
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;

    if (1 == __nobjs) return(__chunk);  //实际取得1,交给客户

     //以下开始将所得区块挂上free-list
    __my_free_list = _S_free_list + _S_freelist_index(__n);

    /* Build free list in chunk */  //在chunk内建立free list
      __result = (_Obj*)__chunk;
      *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
      for (__i = 1; ; __i++) {
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);  //所谓切割就是把指针所指出转型为obj,再取其next_obj指针继续行事,一而再三
        if (__nobjs - 1 == __i) {
            __current_obj -> _M_free_list_link = 0;
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
      }
    return(__result);
}

5.chunk_alloc函数

    

/* We allocate memory in large chunks in order to avoid fragmenting     */
/* the malloc heap too much.                                            */
/* We assume that size is properly aligned.                             */
/* We hold the allocation lock.                                         */
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块所需要申请字节大小
    size_t __bytes_left = _S_end_free - _S_start_free;  //战备池pool大小

    if (__bytes_left >= __total_bytes) {  //pool空间大小足以满足20块需求
        __result = _S_start_free;
        _S_start_free += __total_bytes;  //调整(降低)pool水位
        return(__result);
    } else if (__bytes_left >= __size) {  //pool空间只足以满足一块(含)以上需求
        __nobjs = (int)(__bytes_left/__size);  //改变需求数(注意nobjs是pass-by-reference)
        __total_bytes = __size * __nobjs;  //改变需求量(bytes)
        __result = _S_start_free;
        _S_start_free += __total_bytes;  //调整(降低)pool水位
        return(__result);
    } else {  //pool空间不足以满足一块需求
        size_t __bytes_to_get =
      2 * __total_bytes + _S_round_up(_S_heap_size >> 4)  //现在打算system free-store取这么多来灌注
        // Try to make use of the left-over piece.  //首先将pool做充分运用
        if (__bytes_left > 0) {   //如果pool还有空间
            _Obj* __STL_VOLATILE* __my_free_list =  //找出对应转移至#号free-list
                        _S_free_list + _S_freelist_index(__bytes_left);

            //将pool空间编入第#号free-list(肯定只成1区块)

            ((_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);  //从system free-store取这么多,注入pool
        if (0 == _S_start_free) {     //失败!以下试从free-list找区块
            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) {  //该free-list内有可能用区块,以下释放出一块(only)给pool
                    *__my_free_list = __p -> _M_free_list_link;
                    _S_start_free = (char*)__p;   //把free-list内的目前第一块当成pool
                    _S_end_free = _S_start_free + __i;  //把free-list内的目前第一块当成pool
                    return(_S_chunk_alloc(__size, __nobjs));  //然后再试一下
                    // Any leftover piece will eventually make it to the  此时的pool定够供应至少一个区块,而任何残除零头终将被编入当free-list
                    // right free list.
                }
            }
        _S_end_free = 0;    // In case of exception.

           //至此,表示memory已山穷水尽

          //改用第一级,看看oom-handler能否有能力

            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);

              //这会导致抛出异常,或导致memory不足的情况得到改善

             // This should either throw an
            // exception or remedy the situation.  Thus we assume it
            // succeeded.
        }

        //至此,表示已从system free-store成功取得很多memory

         _S_heap_size += __bytes_to_get;  //累计分配量
        _S_end_free = _S_start_free + __bytes_to_get;  //灌注pool(调整尾段)
        return(_S_chunk_alloc(__size, __nobjs));   //再试一次
    }
}

6.G4.9 pool allocator运行观察

需要一个可累计总分配量和总释放量的operator new/delete,除非user直接使用malloc/free,否则都避不开它们,这就可以累计总量。

我们不能观察到malloc真正分配出去的总量(含有所有overhead),不能,除非...

#include <iostream>
#include <list>
#include <ext\pool_allocator.h>
using namespace std;

static long long countNew = 0;
static long long timesNew = 0;

void* myAlloc(size_t size) {
	return malloc(size);
}

void myFree(void* ptr) {
	return free(ptr);
}
inline void* operator new(size_t size) {
	//cout << "global new(),\t" << size << "\t" ;
	countNew += size;
	++timesNew;
	return myAlloc(size);
}

inline void operator delete(void* ptr) {
	//cout << "global delete(),\t" ;
	return myFree(ptr);
}

inline void operator delete(void* ptr, size_t size) {
	cout << "global delete(),\t" ;
	return myFree(ptr);
}

template<typename T>
using listPool = list<T, __gnu_cxx::__pool_alloc<T> >;

int main() {
	
	countNew = 0;
	timesNew = 0;
	listPool<double> lst;
	
	for(int i=0; i<1000000; ++i) {
		lst.push_back(i);
	}
	
	cout << "::countNew" << ::countNew << endl;
	cout << "::timesNew" << ::timesNew << endl;
	
	countNew = 0;
	timesNew = 0;
	
	list<double> lst2;
	for(int i=0; i<1000000; ++i) {
		lst2.push_back(i);
	}
	
	cout << "::countNew" << ::countNew << endl;
	cout << "::timesNew" << ::timesNew << endl;
	
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值