C++ 简单内存池的设计与STL中分配器的设计

简单内存池的设计

内存池设计

  • 内存池的设计思想,每次获取空间时,先看内存池中有没有自由内存,如果有就把自由内存返回,如果没有,就在一次性向堆中malloc一大块内存,分割成很多的小块,返回第一个小块,剩下的作为内存池备用。这样即减少了malloc的次数,也减少的因为cookie的浪费。
class myAllocator {
private:
	//寻找下一块内存的嵌入式指针
    struct obj{
        obj* next;
    };
public:
    //分配内存的函数
    void* allocate(size_t size);
    //回收内存的函数
    void deallocate(void* ptr);
    private:
    //指向自由内存的指针,初始为空
    obj* freestore = nullptr;
    //一次性申请的内存大小
    const int CHUNK = 10;
};
//分配的设计
void* myAllocator::allocate (size_t size) {
    //存放返回地址的指针
    obj* p;
    //把这个大块内存分割成小块,并用嵌入式指针把每一块连起来
    if(!freestore) {
        //总共需要申请的大小
        size_t chunk = size * CHUNK;
        //先用freestore保存这块内存的首地址
        p = (obj*)malloc(chunk);
        freestore = p;
        //分割串联
        for(int i = 1;i <= CHUNK-1; ++i) {
            //next指针指向下一个小块内存空间
            p->next = (obj*)((size_t)p + size);
            p = p->next;
        }
        //最后一个小块的next指向空
        p->next = nullptr;
    }
    //把第一块返回,freestore指向下一块
    p = freestore;
    freestore = p->next;
    return p;
}
//回收的设计
void myAllocator::deallocate(void* ptr) {
    //把需要回收的那块内存块放在内存池的开头
    ((obj*)ptr)->next = freestore;
    freestore = (obj*)ptr;
}

//使用自定义分配器的类
class Foo {
public:
    long L;
    Foo(long x = 1) : L(x) {}
    //定义一个静态的自定义分配器对象
    static myAllocator myalloc;
    //重载静态operator new
    void* operator new(size_t size) {
        return myalloc.allocate(size);
    }
    //重载静态operator delete
    void operator delete(void* ptr) {
        return myalloc.deallocate(ptr);
    }
};
myAllocator Foo::myalloc;

STL标准模板库中的alloc

  • 在gnuC2.9的版本中使用的容器的分配器就是一个非常经典的内存池设计。在前面说的自定义分配器中是以元素为单位,比如int类型就是分配4字节,double就分配9字节。而在gnuc 2.9中的分配器alloc,则是以字节为单位进行分配的。
  • 在分配器里面维护了一条链表,里面装了16个指针,分别对应8字节,16字节,24字节最后到128字节,这些指针指向对应的自由内存,当系统要求分配某个字节,就到对应的指针里去看有没有内存。除了没有指针指向的自由内存,还有一个备用池也会有自由内存。这样吧,我举个具体的申请例子来讲述这个分配器的运行过程。
  • 运行过程:
    • 首先假如我需要申请32字节的空间,分配器就会先去看链表里面第四个也就是对应32字节的位置下面有没有内存,如果没有,就看备用池有没有,如果也没有,就向系统一次性malloc 20 _ 2 _ 32字节的内存,也就是40块 32字节的内存,但是只使用前20个,返回第一块给客户,然后19块放到刚刚说的链表第四个指针下面,剩下20个放在备用池里面。这样下一次如果在申请32字节大小的内存,就会到链表第四个指针下找刚刚放上去的空间。
    • 然后加入我又要申请64字节,刚刚是32字节,现在是64字节,分配器也是会先到对应链表里面第八个指针看上面有没有自由空间,没有,就看备用池里面有没有,备用池里面有刚刚额外申请的20块32字节的空间,这个时候就会把这些空间先分成每块64字节,应该是10块把,返回第一块给客户,剩下9块挂在第八个指针下面。
    • 大概的运行过程就是这样,当然还有一些细节,就是刚刚说的每次malloc会额外申请20块内存作为备用池的自由内存,细节就是这个申请还有一个追加量,会根据你累计申请空间量,动态调整追加量,越申请,每次追加量就越多。这其实就和vector容器每次扩容是倍增的原理一样,总共申请的越多说明之后可能还会要求更多。
    • 当然还有其他细节,比如备用池最多分出20块对应的内存,还有当备用池不够作为一块内存时会先把这个碎片放到对应的链表上在申请空间等等,当时总之alloc内存池的设计大概是这样,当然我只是学习其中原理,并没有手动实现过这个内存池。最后要说的是,这个优秀的内存池设计背后在gnuC 4.9 被放弃了专用没有任何特殊设计的普通分配器了,我也不知道具体的原因。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值