您的位置:首页 > 其它

[读书笔记]STL源码剖析

2012-12-12 15:42 225 查看
内存池STL实现:
内存池(Memory Pool)是一种内存分配方式。 通常我们习惯直接使用new、malloc等API申请分配内存,这样做的缺点在于:由于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。
  内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率得到提升。
  在内核中有不少地方内存分配不允许失败. 作为一个在这些情况下确保分配的方式, 内核开发者创建了一个已知为内存池(或者是 "mempool" )的抽象. 一个内存池真实地只是一类后备缓存, 它尽力一直保持一个空闲内存列表给紧急时使用
free-lists 分别管理大小为 8,16,24,32,40,56,64,72,80,88,96,104,112,120,128 bytes 大小的小额区块,所以当申请小额内存时,内存的需求量会被上调至 8 的倍数,以便于管理。
1、如果freelist上有用户所申请的空间大小的位置上有未用空间,则直接分配给用户
2、如果freelist上该大小的未用块没有了,则从内存池中分配20个该大小的快,连接到freelist上,然后分配一个给用户。
3、如果内存池不足以分配20个块,则能分配多少就分配多少;如果一个都不能分配,1)首先把内存池剩下的空间交给对应的freelist 2)然后用malloc申请新的内存池空间。
4、如果malloc申请失败,则查找freelist更大的位置上有没空的,如有则直接分配一个给用户。如没有则呼叫第一级配置器。。。
STL内存管理:如申请的内存大于128字节则直接申请,如小于就*8后使用内存池申请。

using namespace std;
class Block
{
friend class MemPool;
size_t size;//块大小,字节为单位
bool use;
char *data;//初始为NULL,代表还没分配空间
public:
Block(){use=false;data=NULL;}
};

class MemPool;
class List
{
friend class Test;
friend class MemPool;
size_t size;//本表所存块的大小
size_t freeBlock;//表示本List还有多少个空的Block
Block list[20];//每个列表最多有20个block
public:
List(){freeBlock=0;}
};

struct UserMem
{
size_t size;//此次申请的空间大小
char* p;//空间的开始指针
};

class MemPool
{
friend class Test;
List freeList[16];//freelist有15种不同的块大小,从8bytes 到128bytes
char *freeStart;//当前内存池的开头指针
size_t poolSize;//内存池的余量
char* poolStart[10];//新建内存池开始的地址,用于最后删除内存池时delete用,最多10个
public:
MemPool();
UserMem myMalloc(size_t size);//客户申请空间函数;默认size为8的倍数
void myFree(const UserMem &usermem);
~MemPool();
};

MemPool::~MemPool(){
//清楚所有内存池空间
for(int i=0;i<10;++i){
if(poolStart[i]!=NULL)
delete []poolStart[i];
}
}
MemPool::MemPool(){
freeStart=NULL;
poolSize=0;
int tmp=8;
//初始化size
for(int i=0;i<16;++i){
freeList[i].size=tmp;
tmp+=8;
}
//初始化内存池开始地址为NULL,没分配地址的内存池地址为NULL
for(int i=0;i<10;++i){
poolStart[i]=NULL;
}
}

UserMem MemPool::myMalloc(size_t size){
//查找freelist上有没可用的块
List *thisList=&freeList[size/8-1];//这个大小的块所在的list
UserMem returnVal;
//freelist上有可用的
if(thisList->freeBlock>0){
for(int i=0;i<20;++i){
if(thisList->list[i].use || thisList->list[i].data==NULL){continue;}
else{//找到还没用的Block
thisList->list[i].use=true;
thisList->freeBlock--;
returnVal.size=size;
returnVal.p=thisList->list[i].data;
return returnVal;
}
}
}

//freelist上没有可用的
//从内存池调配空间给freelist
if(poolSize>=size*5){
//直接拨5个
int count=0;
for(int i=0;i<20;++i){
if(count<5 && thisList->list[i].data==NULL && poolSize>0){
thisList->list[i].data=freeStart;
freeStart+=size;
poolSize-=size;
thisList->freeBlock++;
count++;
}
}
return (myMalloc(size));
}else if(poolSize>=size){
//给1个或以上
int nHasSize=poolSize/size;//可以给nHasSize个
int count=0;
for(int i=0;i<20;++i){
if(count<nHasSize && thisList->list[i].data==NULL && poolSize>0){
thisList->list[i].data=freeStart;
freeStart+=size;
poolSize-=size;
thisList->freeBlock++;
count++;
}
}
return (myMalloc(size));
}else{
//一个都不能给,把内存池剩余容量给freelist
cout<<"内存池还剩下"<<poolSize<<"个字节的内存"<<endl;
if(poolSize>0){

List *returnToList=&freeList[poolSize/8-1];
for(int i=0;i<20;++i){
if(returnToList->list[i].data==NULL){
returnToList->list[i].data=freeStart;
returnToList->freeBlock++;
poolSize-=returnToList->size;
break;
}
}
}
cout<<"内存池还剩下"<<poolSize<<"个字节的内存"<<endl;
//给内存池new10个size给内存池,然后调用自己。
freeStart=new char[size*10];
if(freeStart==NULL){
cout<<"new failed"<<endl;
return (UserMem){0,NULL};
}
for(int i=0;i<10;++i){
if(poolStart[i]==NULL){
poolStart[i]=freeStart;
break;
}
}
poolSize+=size*10;
return (myMalloc(size));
}
}

void MemPool::myFree(const UserMem &usermem){
List *thisList=&freeList[usermem.size/8-1];
int index=((int)usermem.p-(int)thisList->list[0].data)/usermem.size;
thisList->list[index].use=false;
thisList->freeBlock++;
}

class Test
{
public:
static void test(const MemPool &memPool);
static UserMem useMem(MemPool &memPool,size_t size);
static void freeMem(MemPool &memPool,const UserMem &userMem);
};
void Test::test(const MemPool &memPool){//内存池的测试函数
//打印内存池的大小
cout<<"内存池大小为:"<<memPool.poolSize<<endl;
//打印freelist的使用情况
for(int i=0;i<16;++i){
const List *thisSizeList=&memPool.freeList[i];
//打印此大小的list的信息
cout<<"大小为"<<thisSizeList->size<<"的表还剩"<<thisSizeList->freeBlock<<"个块可用"<<endl;
}
}

UserMem Test::useMem(MemPool &memPool,size_t size){
UserMem rVal=memPool.myMalloc(size);
char *p=rVal.p;
if(p!=NULL){
memset(p,'a',size-1);
p[size-1]='\0';
cout<<p<<endl;
}else{
cout<<"error"<<endl;
}
return rVal;
}

void Test::freeMem(MemPool &memPool,const UserMem &userMem){
memPool.myFree(userMem);
}

int main()
{
MemPool mem;
Test::test(mem);
Test::freeMem(mem,Test::useMem(mem,8));
Test::useMem(mem,128);
Test::test(mem);
return 0;
}


list:

就算列表为空,也回存在一个空空节点,end()就是指向这个空节点。这个空节点往后连接到最后一个元素,往前连接到begin(),因此用一个成员endNode来记录这个空节点的迭代器,就可以方便的实现很多操作。如begin()为endNode->next();end()为endNode等等。并且此设计符合STL的前闭后开的规定,比如一个函数要求传入一个迭代器区间,则(beging(),end()),表示所有元素。

deque:
双向队列也是一个随机存取容器。但与vector不同,它的元素并不存储在连续的内存中,只是存储在分段连续的内存中,就是连接多段连续内存。每次连续空间不足时,并不需要搬运数据,只需在寻找一个新的连续内存,并把它连接到之前的内存上就可以了。这个可以避免搬运内存而带来的低效,但代价是复杂的迭代器设计。
deque使用一块连续地址的map存放各段连续的缓冲空间的头指针。缓冲空间才是deque真正的数据存储区。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: