编程论坛

 找回密码
 立即注册
广告联系qq1031180668广告位
查看: 129|回复: 1

[c/c++] C++简单实现GC和内存池

[复制链接]
  • ta_mind

    2018-8-17 21:57
  • classn_01: 71 classn_02

    [LV.6]常住居民II

    981

    主题

    1702

    帖子

    3331

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    3331

    最佳新人活跃会员热心会员推广达人宣传达人灌水之王突出贡献优秀版主荣誉管理论坛元老

    发表于 2018-6-19 20:33:03 | 显示全部楼层 |阅读模式
    C++简单实现GC和内存池
      

        为了避开传统的new\delete创建堆对象的手法,如果使用不当指针容易出现问题,所以简单的实现了引用计数垃圾回收,使用起来貌似还不错。Bug还有待测试。
      最终效果:
        通过DND_GC_CLASS宏来定义一个Student类后,用户还是能通过正常的方法使用Student类。但宏额外生成了HStudent类,通过这个类就能方便的实现自动垃圾回收(类似C#中引用变量的效果)。
    [C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
    #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;
        }
    };


      首先定义一个空引用,它未指向任何值:
    [C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
    HStudent stu;


    调用Initalize()实例化对象,这里实际分配内存空间:
    [C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
    stu.Initalize();

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

        首先实现一个简单的动态数组模板类Vector:
    [C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
        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 =new T[m_size];
                memcpy_s(temp,m_size, m_vector,m_size);
                delete[] m_vector;
                m_vector = temp;
            }  
        };
     


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

    [C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
    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<(const GCObject& b)
        {
            return true;
        }
    };
    


    GCObject类主要包含引用计数变量,使用此框架的类都应该继承它。DND_GC_CLASS宏就让参数继承它,并为其生成一个H开头的句柄类。
    [C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
    #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*operator new(size_tsize){};\
        static voidoperator delete(void*ptr){};\
    };\
    class ClassName : privateGCObject\
    {\
        friend classH##ClassName;\
    

    最后我做了一下测试,对比了和直接new/delete的速度差距:
    [C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
    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;
    



    不积跬步,无以至千里

    classn_11

    0

    主题

    42

    帖子

    88

    积分

    注册会员

    Rank: 2

    积分
    88
    发表于 2018-7-5 12:53:06 | 显示全部楼层
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    手机版|Archiver|小黑屋|sitemap|编程论坛 - 一个单纯的编程学习交流论坛 ( 豫ICP备15032706号 )

    GMT+8, 2018-9-26 13:33 , Processed in 1.097993 second(s), 24 queries .

    Powered by Discuz! X3.4

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表