#include<malloc.h>
#include<iostream>
using namespace std;
class memory_pool
{
private:
static const size_t min_obj_size = 8;
static const size_t max_obj_size = 128;
char * start_free; //内存池起始地址
char * end_free; //内存池结束地址
size_t heap_size; //记录从堆中分配到内存池的总数量
union obj //一个区块单元设计,有下一个区块链表和首地字节址共享
{
union obj * next_obj;
char data;
};
obj * free_list[16]; //16种不同大小的区块的链表指针数组
//可以设计成单例模式,只有一个内存池对象,私有构造
memory_pool()
{
start_free = 0;
end_free = 0;
}
~memory_pool()
{
//释放成员函数申请的堆内存
}
public:
//获取线程池类的单实例
static memory_pool* getInstance()
{
static memory_pool instance;
return &instance;
}
//将要申请的n字节升级到最近的8的倍数
size_t up_to_8s(const size_t &n) const
{
return (n + min_obj_size - 1)&(~(min_obj_size - 1)); //(n+7)&11111000,即后3位清零为8的倍数
}
//根据n获取对应的链表位置
size_t free_index(const size_t &n) const
{
return up_to_8s(n) / min_obj_size - 1; //链表位置从0开始到15
}
//重点给用户调用的分配内存的函数,C风格只提供字节数
void * allocate(const size_t &n)
{
//如果申请的内存大于最大区块,那么就直接从malloc获取
if (n>max_obj_size)
{
return malloc(n);
}
//获取对应链表的首地址
obj * list_head = free_list[free_index(n)];
obj * result = list_head;
if (NULL == result)
{
//若是没有获取到链表区块,则要重新填充该链表
void * ret = refill(up_to_8s(n));
return ret;
}
//更新链表首地址
free_list[free_index(n)] = result->next_obj;
return result;
}
//释放回内存给对应的链表
void deallocate(void *p, const size_t n)
{
//如果大于128,说明是从堆中分配的,要还给堆
if (n>max_obj_size)
{
free(p);
}
obj * list_head = free_list[free_index(n)];
obj * new_head = (obj*)p;
//将原链表头作为新表头的下一个元素
new_head->next_obj = list_head;
//新表头放到链表首部位置
free_list[free_index(n)] = new_head;
}
//重新从内存池中获取内存填充一个区块大小为n的链表
void * refill(const size_t n)
{
int nobjs = 20; //默认一次获取20个相应区块
//从内存池中获取这一整块n*nobjs的内存,传入的nobjs可能被修改为实际获取到的值
char * chunk = chunk_alloc(n, nobjs);
if (1 == nobjs)
{
//说明从内存池也只获取到了一个区块,直接返回给需要的
return chunk;
}
//从内存池中获取了多个区块,则要重新链接起来
obj * list_head = free_list[free_index(n)];
obj *result = (obj*)chunk;
obj *current_obj, *next_obj;
free_list[free_index(n)] = next_obj = (obj*)(chunk + n);
for (int i = 1; ; i++)
{
current_obj = next_obj;
next_obj = (obj*)((char*)next_obj + n);
if (nobjs-1 == i)
{
current_obj->next_obj = NULL;
break;
}
else
{
current_obj->next_obj = next_obj;
}
}
return result; //返回的是大区块首地址
}
//从内存池中获取大块内存
char * chunk_alloc(const size_t n, int &nobjs)
{
char* result;
size_t total_bytes = n*nobjs;
size_t bytes_left = end_free - start_free;
//如果内存池中剩余字节数能满足需求那就先分配
if (bytes_left>=total_bytes)
{
result = start_free; //返回结果就是内存池首地址
start_free = start_free + total_bytes; //内存池新的首地址
return result;
}
//内存池中的剩余字节还够至少一个小区快
else if (bytes_left>n)
{
nobjs = bytes_left / n; //能获取的区块个数
total_bytes = n*nobjs;
result = start_free;
start_free = start_free + total_bytes;
return result;
}
//一个小区快都不够,则要重新从堆中往内存池分一大块内存
else
{
//需要从内存池中获取的内存是当前需求的两倍,以及加上从堆空间分配的总字节数相关的一个附加值
size_t bytes_to_get = 2 * total_bytes + up_to_8s(heap_size >> 4);
//先把内存池之前剩余的小部分分到合适的链表上
if (bytes_left>0)
{
deallocate(start_free, bytes_left); //回收到链表中
}
//重新从堆空间获取内存
start_free = (char *)malloc(bytes_to_get);
if (NULL == start_free)
{
//说明堆空间都没有内存可分配了,则要从已分配的链表中找到最大的空闲区块分配过来
for (int i = max_obj_size / min_obj_size; i >= 0; i--)
{
obj* list_head = free_list[i];
if (NULL != list_head)
{
//说明找到了一个大区块
obj* p = list_head;
//将这个区块放到内存池
start_free = (char*)p;
end_free = start_free + (i + 1)*min_obj_size;
free_list[i] = p->next_obj;
return chunk_alloc(n, nobjs); //重新调整内存池后递归调用
}
}
//说明所有地方都没有内存可取
end_free = start_free = 0;
cerr << "out of memory !" << endl;
exit(1); //退出程序
}
heap_size += bytes_to_get; //更新从堆中分配给内存池的总数量
end_free = start_free + bytes_to_get;
//内存池已更新,则需要递归调用次函数重新分配区块到链表
return chunk_alloc(n, nobjs);
}
}
};
//测试代码
int main()
{
memory_pool *pMpool = memory_pool::getInstance();
double *d = (double*)pMpool->allocate(sizeof(double));
*d = 3.1415926;
cout << *d << endl; //打印出 3.1415926
pMpool->deallocate(d, sizeof(double));
cout << "success!!!" << endl; //打印出success
}
网友评论