zoukankan      html  css  js  c++  java
  • C++中的垃圾回收和内存管理

    最开始的时候看到了许式伟的内存管理变革系列,看到性能测试结果的时候,觉得这个实现很不错,没有深入研究其实现。现在想把这个用到自己的一个项目中来,在linux下编译存在一些问题,所以打算深入研究一下。

    讨论C++内存管理的有两个主要的博客,一个是许式伟的系列,(CSDN: http://blog.csdn.net/xushiweizh/article/details/1388982,许式伟个人空间:http://xushiwei.com/gc-allocator-summary);另一个是CppExplore的一个内存管理系列(http://www.cppblog.com/CppExplore/archive/2008/02/18/42890.html). 主要来研究许式伟的内存管理。

    学习许式伟的内存管理器实现,参考
    http://cplusplus.wikidot.com/cn:memory-management-innovation

    gc_1
       simple_gc.h
       test.cpp
    实现了系列第一篇文章中讲到的内容,一个基本的Allocator,对malloc的封装,用于申请内存,利用new可以在指定位置申请内存,实现新的new函数,在allocator申请的内存上分配内存。
    如果所有的内存都是从某个allocator申请的,那么释放allocator的内存,就释放掉了之前所有在它上面分配的内存。但这个simple_gc只是概念上的,没有实现memory pool的管理。所以接下来看AutoAlloc.

    gc_2
        auto_alloc.h
        auto_alloc.cpp
        test.cpp
    实现了系列第二篇文章中讲到的内容,一个包含memory block管理的Allocator,被其称之为最袖珍的垃圾回收器--AutoAlloc.由于他写文章之后又对代码有部分修改,但又没有修改完全,文章中的代码片段需要进行修正和补充,才能进行编译。每个memory block大小为2048字节,对于小于这个大小的对象,可以直接从这个block里面分一部分,如果剩余部分还足够大,可以给新的对象,那么就继续分。如果无法满足要求,则申请新的block,或者更大的block。所以有些block没有被完全利用,产生一些碎片。由于申请的一块可以给很多对象使用,不需要每个对象申请一次,所以申请内存的效率比较高。但是这个内部需要维护memory block的链表,析构函数的链表,及碎片的存在,会有一些浪费。

    gc_1和gc_2的代码,

    // simple_gc.h
    #ifndef SIMPLE_ALLOC_H_
    #define SIMPLE_ALLOC_H_
    
    #include <cstdlib>    /* malloc, free, rand */
    #include <new>
    
    template <class Type>
    struct DestructorTraits
    {
        static void Destruct(void* pThis)
        {
            ((Type*)pThis)->~Type();
        }
    };
    
    typedef void (*FnDestructor)(void* pThis);
    
    
    class SimpleAlloc
    {
    public:
        //注意这里提供的参数fnDestroy,它是为那些具备垃圾回收能力的allocator需要提供。
        void* Alloc(size_t cb, FnDestructor fnDestroy = NULL)
        {
            return malloc(cb);
        }
     
        //注意这里有看似多余的参数cb,这完全是为了和后续提供的allocator规格一致的需要。
        void Free(void* data, size_t cb)
        {
            free(data);
        }
    };
    
    // 类似于new Type
    template <class Type, class AllocType>
    inline Type* New(AllocType& alloc)
    {
        void* obj = alloc.Alloc(sizeof(Type), DestructorTraits<Type>::Destruct);
        return new(obj) Type;
    }
    
    // 类似于new Type(arg1)
    template <class Type, class ArgType1, class AllocType>
    Type* New(ArgType1 arg1, AllocType& alloc)
    {
        void* obj = alloc.Alloc(sizeof(Type), DestructorTraits<Type>::Destruct);
        return new(obj) Type(arg1);
    }
    
    // 类似于new Type[count]
    template <class Type, class AllocType>
    Type* NewArray(size_t count, AllocType& alloc)
    {
        void* obj = alloc.Alloc(sizeof(Type)*count, DestructorTraits<Type>::Destruct);
        return new(obj) Type[count];
    }
    
    
    #endif /* SIMPLE_ALLOC_H_ */

    auto_alloc.h和auto_alloc.cpp

    #ifndef AUTO_ALLOC_H_
    #define AUTO_ALLOC_H_
    
    #include <cstdlib>    /* malloc, free, rand */
    #include <new>
    
    
    typedef void (*FnDestructor)(void* pThis);
     
    /*
    class AutoAlloc
    {
    public:
        ~AutoAlloc();                                  // 析构函数。自动调用Clear释放内存
        void* allocate(size_t cb);                     // 类似于malloc(cb)
        void* allocate(size_t cb, FnDestructor fn);    // 申请内存并指定析构函数
        void clear();                                  // 析构并释放所有分配的对象
    };
    */
    
    class AutoAlloc
    {
    public:
        enum { BlockSize = 2048 };
    private:
        struct _MemBlock
        {
            _MemBlock* pPrev;
            char buffer[BlockSize];
        };
        enum { HeaderSize = sizeof(_MemBlock) - BlockSize };
     
        char* m_begin;
        char* m_end;
    
        _MemBlock* _ChainHeader() const
        {
            return (_MemBlock*)(m_begin - HeaderSize);
        }
    
        struct _DestroyNode
        {
            _DestroyNode* pPrev;
            FnDestructor fnDestroy;
        };
        _DestroyNode* m_destroyChain;
    
    public:
        AutoAlloc();
        ~AutoAlloc();                                  // 析构函数。自动调用Clear释放内存
        void* Alloc(size_t cb);                     // 类似于malloc(cb)
        void* Alloc(size_t cb, FnDestructor fn);    // 申请内存并指定析构函数
        void Clear();                                  // 析构并释放所有分配的对象
        
    };
    
    template <class Type>
    struct DestructorTraits
    {
        static void Destruct(void* pThis)
        {
            ((Type*)pThis)->~Type();
        }
    };
    
    // 类似于new Type
    template <class Type, class AllocType>
    inline Type* New(AllocType& alloc)
    {
        void* obj = alloc.Alloc(sizeof(Type), DestructorTraits<Type>::Destruct);
        return new(obj) Type;
    }
    
    // 类似于new Type(arg1)
    template <class Type, class ArgType1, class AllocType>
    Type* New(ArgType1 arg1, AllocType& alloc)
    {
        void* obj = alloc.Alloc(sizeof(Type), DestructorTraits<Type>::Destruct);
        return new(obj) Type(arg1);
    }
    
    // 类似于new Type[count]
    template <class Type, class AllocType>
    Type* NewArray(size_t count, AllocType& alloc)
    {
        void* obj = alloc.Alloc(sizeof(Type)*count, DestructorTraits<Type>::Destruct);
        return new(obj) Type[count];
    }
    
    #endif /* AUTO_ALLOC_H_ */
    #include "auto_alloc.h"
    
    #include <cstdio>
    
    AutoAlloc::AutoAlloc()
    {
          m_begin = m_end = (char*)HeaderSize;
        m_destroyChain = NULL;
    }
    
    void* AutoAlloc::Alloc(size_t cb)
    {
        if(m_end - m_begin < cb)
        {
            if (cb >= BlockSize)
            {
                    _MemBlock* pHeader = _ChainHeader();
                    _MemBlock* pNew = (_MemBlock*)malloc(HeaderSize + cb);
                    if (pHeader)
                    {
                        pNew->pPrev = pHeader->pPrev;
                        pHeader->pPrev = pNew;
                    }
                    else
                    {
                        m_end = m_begin = pNew->buffer;
                        pNew->pPrev = NULL;
                    }
                    return pNew->buffer;        
            }
            else
            {
                _MemBlock* pNew = (_MemBlock*)malloc(sizeof(_MemBlock));
                pNew->pPrev = _ChainHeader();
                m_begin = pNew->buffer;
                m_end = m_begin + BlockSize;
            }
        }
        return m_end -= cb;
    }
    
    void AutoAlloc::Clear()
    {
    
        // destroy
        printf("destroy
    ");
        while (m_destroyChain)
        {
            m_destroyChain->fnDestroy(m_destroyChain + 1);
            m_destroyChain = m_destroyChain->pPrev;
        }
        
        // free memory
        printf("free memory
    ");
        _MemBlock* pHeader = _ChainHeader();
        while (pHeader)
        {
            _MemBlock* pTemp = pHeader->pPrev;
            free(pHeader);
            pHeader = pTemp;
        }
        m_begin = m_end = (char*)HeaderSize;
    }
    
    void* AutoAlloc::Alloc(size_t cb, FnDestructor fn)
    {
        _DestroyNode* pNode = (_DestroyNode*)Alloc(sizeof(_DestroyNode) + cb);
        pNode->fnDestroy = fn;
        pNode->pPrev = m_destroyChain;
        m_destroyChain = pNode;
        return pNode + 1;
    }
    
    AutoAlloc::~AutoAlloc()
    {
        Clear();
    }

    测试代码这里给出一份就可以了,

    //#include "simple_gc.h"
    #include "auto_alloc.h"
    
    #include <cstdio>
    
    class MyClass
    {
    public:
        int a;
    public:
        MyClass(int t=52):a(t){}
        ~MyClass(){}
        void Print()
        {
            printf("%d
    ",a);
        }
    };
    
    
    int main()
    {
    
    //    SimpleAlloc alloc;
        AutoAlloc alloc;    
        int count = 5; 
    
        int* intArray = NewArray<int>(count, alloc);
     
        for(int i=0;i<count;i++)
        {
            intArray[i] = i+1;
        }
        for(int i=0;i<count;i++)
        {
            printf("%d ",intArray[i]);
        }
        printf("
    ");
    
        MyClass* obj = New<MyClass>(alloc);
        
        obj->Print();
    
        int arg1 = 1024;
        MyClass* objWithArg = New<MyClass>(arg1, alloc);
    
         objWithArg->Print();
    
        alloc.Clear();
    
    //    MyClass* objArray = NewArray<MyClass>(count, alloc);
    
        return 0;
    }

    gc_3
    这里只是介绍了AutoFreeAlloc的适用情况。AutoFreeAlloc的内存是在最后一次性释放的。在有些情况中,有些对象已经不需要,
    需要手动释放,避免大量内存被占用,这个时候AutoFreeAlloc就不适合了。

    gc_4
    这里介绍boost::object_pool. boost::object_pool管理某一类对象,支持手动释放内存,对于没有手动释放的内存,这个object_pool会遍历
    所有的block,确定是否已经是自由内存,如果不是,则进行回收。
    allocator只是内存管理器,而 gc allocator则是指具有垃圾回收功能的内存管理器。

    gc_5
    这里评论了智能指针和其它垃圾回收器的使用情况。

    gc_6
    这里介绍了ScopeAlloc,给出了很多代码片段,不容易组织称完整的代码。但可以大致了解ScopeAlloc的原理。
    在AutoFreeAlloc中,内置了一个memory block的链表,来管理内存。而在ScopeAlloc中,向BlockPool申请内存。
    相比于AutoFreeAlloc,适用范围更广,速度更快。

    gc_7
    将ScopeAlloc用于STL时,需要将ScopeAlloc封装一层,得到StlAlloc.

    gc_8
    这里介绍了多线程环境中的gc allocator。推荐的方式是,每个线程有自己的allocator,但是都向同一个memory pool申请内存。
    这里要求memory pool是多线程安全的,互斥申请内存。

    虽然他的代码可以下载,但我在linux使用的时候出现各种问题,由于他包含了多线程模型的支持,代码也复杂了一些,需要继续研究。 

     代码可以在linux下编译,在boost-memory-xx/libs/memory/build/下面有Makefile文件,Makefile.li32表示linux 32bit版本的makefile. 

    在编译之前还是需要做几个小的修改,一个是关于memcpy,在basic.hpp里面添加#include <cstring>,另一个是_alloca函数,添加#include <cstdlib>。

    然后编译得到的为动态库文件,libboost-memory.so,在build/Debug下面。

  • 相关阅读:
    非标准的xml解析器的C++实现:二、解析器的基本构造:语法表
    非标准的xml解析器的C++实现:一、思考基本数据结构的设计
    lua5.4 beta中的to-be-closed变量的用法
    lua table与json的之间的互相转换高性能c++实现
    lua多线程共享数据的解决方案
    winsock完成端口套接字重用注意事项
    Less相关的用法以及Vue2.0 中如何使用Less
    1:MUI选择器组件抛出“n.getSelectedItem is not a function”异常的解决办法 2:mui三级联动 3:移动端关闭虚拟键盘
    redux状态管理和react-redux的结合使用
    初步学习React Router 4.0
  • 原文地址:https://www.cnblogs.com/Frandy/p/cpp_gc_allocator.html
Copyright © 2011-2022 走看看