内存池的原理及实现

Posted CPP开发者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内存池的原理及实现相关的知识,希望对你有一定的参考价值。


来自:博客园-啊汉

链接:http://www.cnblogs.com/hlxs/p/3391698.html


在软件开发中,有些对象使用非常频繁,那么我们可以预先在堆中实例化一些对象,我们把维护这些对象的结构叫“内存池”。在需要用的时候,直接从内存池中拿,而不用从新实例化,在要销毁的时候,不是直接free/delete,而是返还给内存池。


把那些常用的对象存在内存池中,就不用频繁的分配/回收内存,可以相对减少内存碎片,更重要的是实例化这样的对象更快,回收也更快。当内存池中的对象不够用的时候就扩容。


我的内存池实现如下:


#pragma once

#include <assert.h>


template<typename T>

struct ProxyT

    ProxyT():next(NULL){} 

    T data;

    ProxyT* next;

};


template<typename T>

class MemoryPool

{

public:

    static void* New()

    {

        if(next==NULL)

        {

            Alloc();

        }

        assert(next!=NULL);

        ProxyT<T>* cur=next;

        next=next->next;

        return cur;

    }


    static void Delete(void* ptr)

    {

        ProxyT<T>* cur=static_cast<ProxyT<T>*>(ptr);

        cur->next=next;

        next=cur; 

    }


#ifdef CanFree

    static void Clear()

    {

        ProxyT<T>* proxy=NULL;

        while(next!=NULL)

        {

            proxy=next->next;

            delete next;

            next=proxy->next;

        }

        next=NULL;

    }

#endif

    

private: 

    static void Alloc(size_t size=16)

    {

        if(next==NULL)

        {

        #ifdef CanFree

            ProxyT<T>* tmpProxy=new ProxyT<T>();

            next=tmpProxy;

            for(int i=1;i<size;i++)

            { 

                tmpProxy->next=new ProxyT<T>();

                tmpProxy=tmpProxy->next;

            } 

        #else

            ProxyT<T>* memory=(ProxyT<T>*)malloc(size*sizeof(ProxyT<T>));

            ProxyT<T>* tmpProxy=new (memory) ProxyT<T>();

            next=tmpProxy;

            for (size_t i=1;i<size;i++)

            {

                tmpProxy->next=new (memory+i) ProxyT<T>();

                tmpProxy=tmpProxy->next;

            }

        #endif


        }

    }

 

    static ProxyT<T>* next; 

    MemoryPool<T>();

    MemoryPool<T>(const MemoryPool<T>&);

};


template<typename T> ProxyT<T>* MemoryPool<T>::next=NULL; 


#define NewAndDelete(className)             

static void* operator new(size_t size)      

{                                           

    return MemoryPool<className>::New();    

}                                           

static void operator delete(void* ptr)      

{                                           

    MemoryPool<className>::Delete(ptr);     

}   


测试代码如下:


#include "stdafx.h" 

#define CanFree

#include "MemoryPool.h"

 

struct A

    int i; 

    NewAndDelete(A) 

};

  

int _tmain(int argc, _TCHAR* argv[])

{   

     

    { 

        vector<A*> vect;

        for(int i=0;i<16;i++)

        {

            A* a=new A();

            a->i=i;

            vect.push_back(a);

        }

        for(int i=0;i<vect.size();i++)

        {

            cout<<vect[i]->i<<endl;

        }

        for(int i=vect.size()-1;i>=0;i--)

        {

            delete vect[i];

        }

        vect.clear();

        

        MemoryPool<A>::Clear();

    }

   

    system("pause");

    return 0; 

}


运行结果如下图:




所有要使用内存池的对象,只需要在这个对象中引入宏NewAndDelete,这个宏其实就是重写对象的new和delete方法,让对象的创建和回收都通过内存池来实现,所有用内存池实现的对象使用起来和别的对象基本上是一样,唯一的一个问题就是内存池对象对象不是线程安全的,在多线程编程中,创建一个对象时必须枷锁。如果在New和Delete的实现中都加个锁,我又觉得他太影响性能,毕竟很多时候是不需要枷锁,有些对象可能有不用于多线程,对于这个问题,求高手指点!



以上是关于内存池的原理及实现的主要内容,如果未能解决你的问题,请参考以下文章

固定内存块尺寸的内存池原理及代码

应用安全一个轻量级内存池的实现与细节

从内核源码看 slab 内存池的创建初始化流程

nginx源代码分析之内存池实现原理

线程池的设计原理是什么?

阿里P7面试题及答案