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\n");
    while (m_destroyChain)
    {
        m_destroyChain->fnDestroy(m_destroyChain + 1);
        m_destroyChain = m_destroyChain->pPrev;
    }
    
    // free memory
    printf("free memory\n");
    _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\n",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("\n");

    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下面。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM