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 {_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;
}