请选择 进入手机版 | 继续访问电脑版
微信扫一扫 分享朋友圈

已有 823 人浏览分享

开启左侧

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

[复制链接]
823 1
C++简单实现GC和内存池
  

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

  首先定义一个空引用,它未指向任何值:
[mw_shl_code=cpp,true]HStudent stu;[/mw_shl_code]

调用Initalize()实例化对象,这里实际分配内存空间:
[mw_shl_code=cpp,true]stu.Initalize();[/mw_shl_code]
当stu指向的堆内存没有任何H句柄对象指向时就会被内存池标记为可用,从而给予其它对象使用。Student的成员函数通过->操作符访问。
具体实现:

    首先实现一个简单的动态数组模板类Vector:
[mw_shl_code=cpp,true]    template<typenameT>
    class Vector
    {
    public:
        T& operator[](unsignedi)
        {
            assert(i <m_size);
            return m_vector;
        }
        T& At(unsignedi)
        {
            while(i >=m_size)
                _extend();
            return m_vector;
        }
        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;
        }  
    };
[/mw_shl_code]

  接着实现内存池模板类,包含一个含vector节点的链表。当程序需要更多内存时,就会创建新的Vector节点。每个节点依次是2^(n-1)个元素。如果用户需要一个内存,就通过查询内存所在变量的引用计数是否为0,是就说明没人使用,就返回此地址。
[mw_shl_code=cpp,true]    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));
                    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;
            }
        }
    };
[/mw_shl_code]
接着写个接口让程序来调用,其中staticPoolArray<T> a用来存储内存池用到的内存:
[mw_shl_code=cpp,true]    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();
        }
    };
[/mw_shl_code]
如果代码中想要分配指定类型的一块内存就应该调用Mempool::GetSite<TypeName>()。但规定是T得继承于GCObject类。

[mw_shl_code=cpp,true]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;
    }
};
[/mw_shl_code]

GCObject类主要包含引用计数变量,使用此框架的类都应该继承它。DND_GC_CLASS宏就让参数继承它,并为其生成一个H开头的句柄类。
[mw_shl_code=cpp,true]#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;\
[/mw_shl_code]
最后我做了一下测试,对比了和直接new/delete的速度差距:
[mw_shl_code=cpp,true]DWORD t1 = GetTickCount();
        unsigned i =REPLAY_COUNT;
        while (i--)
        {
            //STU_NUMBER个
            HStudent stu3[STU_NUMBER];
            for (unsignedi = 0; i != STU_NUMBER; ++i)
            {
                stu3.Initalize();
                //stu3->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;
[/mw_shl_code]


不积跬步,无以至千里

评论 1

japanphone  初级码农Ⅰ  发表于 2018-7-5 12:53:06 | 显示全部楼层
{:4_99:}{:4_99:}{:4_99:}{:4_99:}{:4_99:}

举报 使用道具

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

本版积分规则

0

关注

37

粉丝

1150

主题
热门资讯
网友晒图
图文推荐
  • 联系我们
  • 邮箱:admin@c0ks.com(请把#改成@)
  • 电话:18530790808
  • QQ客服 1031180668
  • 工作时间:周一至周五(早上9点至下午5点)
  • 微信二维码

  • 扫描访问手机版

Archiver|手机版|小黑屋|从零开始

GMT+8, 2020-5-26 02:25 , Processed in 0.102322 second(s), 37 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

  • QQ: 1031180668

    客服电话

    18530790808

    电子邮件

    admin@c0ks.com

    在线时间:8:00-16:00