内存池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真正的数据存储区。