zoukankan      html  css  js  c++  java
  • [读书笔记]STL源码剖析

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

  • 相关阅读:
    Python之while循环
    Python之分支语句
    Python之变量
    Python开挂的吧!
    xshell 连接 ubuntu 16.04报错
    js中的script标签
    javascript中的事件学习总结
    【JAVAWEB学习笔记】04_JavaScript
    【JAVAWEB学习笔记】03_JavaScript
    【JAVAWEB学习笔记】02_HTML&CSS
  • 原文地址:https://www.cnblogs.com/iyjhabc/p/2987463.html
Copyright © 2011-2022 走看看