C++简单实现GC和内存池

来源:互联网 发布:淘宝赚钱 编辑:程序博客网 时间:2024/06/15 18:52

  C++简单实现GC和内存池

  本章前言:

    这章就是为了避开传统的new\delete创建堆对象的手法,如果使用不当指针容易出现问题,所以本章简单的实现了引用计数垃圾回收,使用起来貌似还不错。Bug还有待测试。

  最终效果:

    通过DND_GC_CLASS宏来定义一个Student类后,用户还是能通过正常的方法使用Student类。但宏额外生成了HStudent类,通过这个类就能方便的实现自动垃圾回收(类似C#中引用变量的效果)。

#include "DNDGC.h"

DND_GC_CLASS(Student)

public:

    int id;

    char data[40000];

    Student():

    id(0){}

    void Init(intidi)

    {

        id = idi;

    }

    bool operator<(constStudent&b)

    {

        return id <b.id;

    }

};

    首先定义一个空引用,它未指向任何值:

HStudent stu

    调用Initalize()实例化对象,这里实际分配内存空间:

stu.Initalize();

    当stu指向的堆内存没有任何H句柄对象指向时就会被内存池标记为可用,从而给予其它对象使用。Student的成员函数通过->操作符访问。

  具体实现:

    首先实现一个简单的动态数组模板类Vector:

    template<typenameT>

    class Vector

    {

    public:

        T& operator[](unsignedi)

        {

            assert(i <m_size);

            return m_vector[i];

        }

        T& At(unsignedi)

        {

            while(i >=m_size)

                _extend();

            return m_vector[i];

        }

        Vector() :

            m_vector(newT[1]),

            m_size(1){}

        Vector(unsignedsize) :

            m_vector(newT[size]),

            m_size(size){}

        ~Vector()

        {

            delete[] m_vector;

        }

        unsigned GetSize(){return m_size;}

    private:

        T* m_vector;

        unsigned m_size;

        void _extend()

        {

            m_size<<= 1;

            T* temp =newT[m_size];

            memcpy_s(temp,m_size,m_vector,m_size);

            delete[] m_vector;

            m_vector = temp;

        }  

    };

 

    接着实现内存池模板类,包含一个含vector节点的链表。当程序需要更多内存时,就会创建新的Vector节点。每个节点依次是2^(n-1)个元素。如果用户需要一个内存,就通过查询内存所在变量的引用计数是否为0,是就说明没人使用,就返回此地址。

    template<classT>

    class PoolArray

    {

    private:

        //页面组(一个页面就是一个数组而已)

        std::list<Vector<T>*>m_pages;

    public:

        //构造时初始化第一个页,大小为1

        PoolArray()

        {

            //注意类型是指针

            Vector<T>*a =new Vector<T>(1);

            std::cout<<"使用内存:"<< (sizeof(T)) / 1024.0f<<"KB"<< std::endl;

            m_pages.push_back(a);

        }

        //要取一个位置

        T* GetSite()

        {

        loop:

            unsigned num = 0;

            for (autoiter =m_pages.begin();iter !=m_pages.end(); ++iter)

            {

                Vector<T>*a = *iter;

                for (unsignedi = 0;i < a->GetSize(); ++i)

                {

                    GCObject* obj = (GCObject*)&((*a)[i]);

                    if (obj->m_refNum == 0)

                    {

                        obj->m_refNum++;

                        return (T*)obj;

                    }

                }

                num += a->GetSize();

            }

            //没有空位,则添加新页,为以前总和的两倍

            Vector<T>*next =new Vector<T>(num<< 1);

            std::cout<<"使用内存:"<< ((num << 1) *sizeof(T)) /1024.0f<< "KB" << std::endl;

            m_pages.push_back(next);

            goto loop;

        }

        //析构,结束程序的时候调用

        ~PoolArray()

        {

            for (autoiter =m_pages.begin();iter !=m_pages.end(); ++iter)

            {

                Vector<T>*a = *iter;

                std::cout<<"释放内存:"<< (a->GetSize() *sizeof(T)) / 1024.0f<< "KB"<<std::endl;

                delete a;

            }

        }

       

    };

    其中使用内存和释放内存的地方写有调试语句,效果如下:

   

    接着写个接口让程序来调用,其中staticPoolArray<T> a用来存储内存池用到的内存:

    class Mempool

    {

        //用户禁止使用

    private:

        Mempool() {};

        //查找T类型的auto_array

        template<classT>

        static PoolArray<T>&FindPoolArray()

        {

            static PoolArray<T>a;

            return a;

        }

    public:

        //返回指定类型的内存池地址

        template<classT>

        static T*GetSite()

        {

            return FindPoolArray<T>().GetSite();

        }

    };

如果代码中想要分配指定类型的一块内存就应该调用Mempool::GetSite<TypeName>()。但规定是T得继承于GCObject类。

class GCObject

{

    //friend class DND::Mempool;

    template<classT>

    friend classDND::PoolArray;

public:

    unsigned GetRefNum()

    {

        return m_refNum;

    }

    void PrintTypeName()

    {

        std::cout<<typeid(this).name()<<std::endl;

    }

    unsigned m_refNum;

    GCObject() : m_refNum(0){}

    virtual booloperator<(constGCObject& b)

    {

        return true;

    }

};

    GCObject类主要包含引用计数变量,使用此框架的类都应该继承它。DND_GC_CLASS宏就让参数继承它,并为其生成一个H开头的句柄类。

#define DND_GC_CLASS(ClassName)\

class ClassName;\

class H##ClassName\

{\

public:\

    ClassName* operator->(){assert(m_p &&"This is null reference!");return (ClassName*)m_p;}\

    ~H##ClassName(){if(m_p&& !--m_p->m_refNum){m_p = 0;}}\

    H##ClassName(): m_p(0){}\

    H##ClassName(constH##ClassName&b){m_p = (GCObject*)b.m_p;++m_p->m_refNum;}\

    H##ClassName&operator=(constH##ClassName&b){if(m_p&& !--m_p->m_refNum){m_p = 0;}m_p = (GCObject*)b.m_p;++m_p->m_refNum;return *this;}\

    bool operator<(constH##ClassName&b){return *m_p < *b.m_p;}\

    void Initalize(){if(m_p && !--m_p->m_refNum){m_p = 0;} m_p = (GCObject*)DND::Mempool::GetSite<ClassName>();}\

private:\

    GCObject* m_p;\

    static void*operatornew(size_tsize){};\

    static voidoperatordelete(void*ptr){};\

};\

class ClassName : privateGCObject\

{\

    friend classH##ClassName;\

最后我做了一下测试,对比了和直接new/delete的速度差距:

        DWORD t1 = GetTickCount();

        unsigned i =REPLAY_COUNT;

        while (i--)

        {

            //STU_NUMBER

            HStudent stu3[STU_NUMBER];

            for (unsignedi = 0;i != STU_NUMBER; ++i)

            {

                stu3[i].Initalize();

                //stu3[i]->Print();

            }

        }

        DWORD t2 = GetTickCount();

        std::cout<<"花费时间:"<<t2 - t1  << "ms"<<std::endl;

        //////////////////////////////////////////////////////////

        t1 = GetTickCount();

        i = REPLAY_COUNT;

        while (i--)

        {

            Student* stu4 = new Student[STU_NUMBER];

            delete stu4;

        }

        t2 = GetTickCount();

        std::cout<<"花费时间:"<<t2 - t1 << "ms"<<std::endl;

得出的数据如下:

 

作者:略游

日期:17-07-22

QQ:1339484752

原创粉丝点击