1、模拟实现STL源码中实现的空间配置器
实现方式:
STL源码中的空间配置器的实现分为一级与二级空间配置器;
1.1 一级空间配置器
所谓的一级空间配置器:(实现特点)
【1】直接使用malloc函数申请内存
【2】自定义清除函数;(当malloc失败之后,调用清除函数来释放一部分的内存空间,以便malloc的成功)
【3】假设调用清除函数之后,还是malloc失败的话,就需要抛出异常 (bad_alloc)
代码实现:
//实现一级空间配置器
template<int inst>
class __Malloc_Alloc_Template
{
public:
static void* allocate(size_t n)
{
void * result = malloc(n);
//要是malloc失败
if(result == NULL)
{
//调用omm_malloc函数
result = omm_malloc(n);
}
return result;
}
//空间释放
static void deallocate(void * del)
{
//直接调用free
free(del);
}
//设置自定义的清理函数 函数返回的是原来的清理函数
//函数原型的意思就是
//定义一个函数 返回值是 静态的函数指针参数为空
//这个函数的参数为 函数指针参数 为 空 func
static void (*set_malloc_handler(void (* func)()))()
{
void (*old) = ___malloc_alloc_oom_handler;
___malloc_alloc_oom_handler = func;
return old;
}
private:
static void (*___malloc_alloc_oom_handler)();
//表示当前malloc失败内存不足,调用自定义的清理函数
static void * omm_malloc(size_t n)
{
void (*my_malloc_handler)();
void *result =NULL;
while(1)
{
my_malloc_handler = ___malloc_alloc_oom_handler;
//表示的当前的清理函数为空的话
if(my_malloc_handler == NULL)
{
//直接抛出异常
throw bad_alloc();
}
//调用清理函数
my_malloc_handler();
//调用完之后重新malloc来申请空间
result = malloc(n);
if(result)
{
return result;
}
}
}
};
template<int inst>
void (*__Malloc_Alloc_Template<inst>::___malloc_alloc_oom_handler)()=NULL;
1.2 二级空间配置器
实现方式:
在这里引入内存池与自由链表来实现二级空间配置
二级空间配置器的优缺点:
【内存池】
我们都知道,编译器在用户态与内核态之间的转换是十分的耗时的。
所以,我们可以事先申请一大片的内存空间;将它保存起来,等到需要动态开辟空间的时候,直接到这块空间上切一块,
直到用完之后,重新申请;
【自由链表】
何为自由链表?
就是使用链表将所需要大小的内存块链到一块。
SGI二級空间配置器保存的自由链表的空间块大小都是8的倍数;并且会將任何小区域的内存块的大小調至8 的倍數(
各自管理大小分別為 8, 16, 24, 32, 40, 48, 56, 64, 72,80, 88, 96, 104, 112, 120, 128 bytes,
当需要的空间大小超过128byte时,直接调用一级空间配置器。
实现代码:
//实现二级空间配置器
template<bool threads,int inst>
class __Default_Alloc_Template
{
public:
static void * allocate(size_t n)
{
//要是当前要开辟的空间大于128的则直接使用 一级空间配置器来malloc开辟
if(n >(size_t) __MAX_BYTES)
{
return __Malloc_Alloc_Template<0>::allocate(n);
}
//小于128的直接到自由链表中获取
//index:得到大小为n的空间要在自由链表的下标为index处获得
size_t index = FREELIST_INDEX(n);
//result表示的就是自由链表的第一结点;
obj* result = free_list[index];
if(result== NULL)
{
//要是当前result 为NULL 表示的当前的自由链表中没有当前大小的内存块
//需要从内存池中进行切分
return refill(ROUND_UP(n));
}
//表示的当前的自由链表中有此大小的内存块;直接拿到;
//更新当前的自由链表
free_list[index] = result->free_list_link;
return result;
}
//refill函数表示的是要是当前的自由链表中没有此大小的内存块
//需要来从内存池中来切分
static void * refill(size_t n)
{
//表示现在需要到内存池中切分20个这样大小的内存块
size_t nobjs= 20;
//chunk_alloc为切分函数
char * chunk = chunk_alloc(n,nobjs);
//nobjs表示输出函数 返回的是实际切分到的内存块个数
if(nobjs == 1)
{
//要是只切分到一个内存块的话,直接返回就好
return (void *) chunk;
}
//其他就表示的是得到的内存块的个数大于1
//需要将剩余的内存块挂接到的自由链表中
size_t index = FREELIST_INDEX(n);
//cur表示的就去除第一个要返回的内存块,的第二个内存块
obj * cur = (obj*)chunk + n;
free_list[index] = cur;
for(size_t i = 2;i < nobjs;++i)
{
obj * next = (obj*)(chunk + i*n);
cur->free_list_link = next;
cur= next;
}
cur->free_list_link = NULL;
return (void *)chunk;
}
//chunk_alloc函数来从内存池中切分大小为size的内存块 需要的个数为nobjs
//nobjs传引用 ,返回的是实际切分的内存块个数
static char * chunk_alloc(size_t size,size_t & nobjs)
{
char * ret = NULL;
size_t total_bytes = size * nobjs;//表示总共需要切分的内存大小
size_t bytes_left = end_free - start_free;//表示的是当前的内存池的大小
if(bytes_left > total_bytes)
{
//表示的是当前的内存池足够切分当前需要的内存块
ret = start_free;
start_free += total_bytes;//更新内存池的大小
return ret;
}
else if(bytes_left >=size)
{
//表示的是当前的内存池内不足以开辟当前需要的内存块
//但足够开辟至少一个内存块
nobjs = bytes_left /size;//表示的当前切分到的内存块个数
ret =start_free ;
start_free += (nobjs*size);
return ret;
}
else
{
//表示的是当前的内存池一个内存块都不够切分
//则需要重新malloc来开辟空间
if(bytes_left != 0)//当前的内存池大小不为0 ,需要处理剩余的内存
{
//将当前内存池中的空间合理的分配得到自由链表中
int index = FREELIST_INDEX(bytes_left);
//头插到到自由链表中
((obj*)start_free)->free_list_link = free_list[index];
free_list[index] = (obj*)start_free;
}
//重新开辟新空间
size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size>>4);//bytes_to_get表示新开辟空间的大小
start_free = (char*)malloc(bytes_to_get);
if(start_free == NULL)
{
//表示的是malloc失败,系统已经开不出空间
//现在只能从自由链表中来看看
for(size_t i = FREELIST_INDEX(size);i < (size_t)__NFREELISTS;++i )
{
if(free_list[i] != NULL)
{
start_free = (char*)free_list[i];
end_free = start_free + (i+1)*__ALIGN;
return chunk_alloc(size,nobjs);
}
}
end_free = 0 ;
start_free = (char*)__Malloc_Alloc_Template<0>::allocate(bytes_to_get);
}
heap_size = heap_size+ bytes_to_get;
end_free = start_free + bytes_to_get;
return chunk_alloc(size,nobjs);
}
}
static void deallocate(void * p,size_t n)
{
obj * del =(obj*) p;
//要是当前释放的空间大小大于128直接使用一级空间配置器释放
if( n >(size_t)__MAX_BYTES )
{
__Malloc_Alloc_Template<0>::deallocate(p);
return ;
}
//将释放的空间连到自由链表上
size_t index = FREELIST_INDEX(n);
del->free_list_link = free_list[index];
free_list[index] = del;
}
private:
//表示的小型的区域的上调边界
enum {__ALIGN = 8};
//自由链表的链接的内存的最大值
enum {__MAX_BYTES =128};
//表示的是自由链表的个数
enum {__NFREELISTS = __MAX_BYTES / __ALIGN};
//ROUND_UP函数将bytes上调到 8 的倍数
static size_t ROUND_UP(size_t bytes)
{
//任何数+ 7 将这个数 与上 FFF0
return (bytes +__ALIGN-1)& ~(__ALIGN-1);
}
//根据要开辟的空间大小来得到 该大小在自由链表中的下标
static size_t FREELIST_INDEX(size_t bytes)
{
return (bytes + __ALIGN-1)/(__ALIGN-1);
}
private:
//链表的结点
union obj
{
union obj * free_list_link ;//自由链表
char client_data[1];
};
//自由链表数组 ,内部保存一个个的内存块链表
static obj * free_list[__NFREELISTS];
static char* start_free;//内存池的起点
static char* end_free;//内存池的结尾
static size_t heap_size ;//内存池的大小;
};
template<bool threads,int inst>
char *__Default_Alloc_Template<threads,inst>::start_free = 0;
template<bool threads,int inst>
char *__Default_Alloc_Template<threads,inst>::end_free = 0;
template<bool threads,int inst>
typename __Default_Alloc_Template<threads,inst>::obj* __Default_Alloc_Template<threads,inst>::free_list[__Default_Alloc_Template<threads,inst>::__NFREELISTS] = {0};
template<bool threads,int inst>
size_t __Default_Alloc_Template<threads,inst>::heap_size = 0;
2、空间配置器的包装与运用方式
实现:
#ifdef __USE_MALLOC
typedef __Malloc_Alloc_Template<0> alloc;
#else
typedef __Default_Alloc_Template<false,0> alloc;
#endif //__USE_MALLOC
template<class T ,class Alloc =alloc>
class simple_alloc
{
public:
//n表示的是申请T类型的空间的个数
static T * allocate(size_t n)
{
return 0==n ? NULL : (T *)Alloc::allocate(n*sizeof(T));
}
//表示申请一个T类型的空间
static T * allocate(void)
{
return (T*)Alloc::allocate(sizeof(T));
}
static void deallocate(T * del)
{
Alloc::deallocate(del,sizeof(T));
}
static void deallocate(T * del,size_t n)
{
if( n!=0)
Alloc::deallocate(del,sizeof(T)*n);
}
};
#include<vector>
void Test()
{
int * array = simple_alloc<int,alloc>::allocate(2);
array[0]=0;
array[1]=1;
cout<<array[0]<<endl;
cout<<array[1]<<endl;
}