STL第二级空间配置器风格的内存池实现

#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
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • 目录 简介 环境 关键点分析 源码 效果图 遇到的问题 致谢 简介 项目进行一定阶段的时候,需要给团队中的QA同学...
    申申申申申阅读 1,145评论 0 7
  • 有些花儿的出现,似乎总在提醒我们,不要对生命持有任何简单化的理解,大自然可以用各种方式散播种子,也能接纳它们开出各...
    文心访艺阅读 289评论 0 0
  • 文/段晓艾 学习《正面管教》好多年了,也是认证讲师,但是感觉自己还是一直徘徊在亲子教育的大门外,并没有真正走进去。...
    段晓艾阅读 430评论 2 2