最開始的時候看到了許式偉的內存管理變革系列,看到性能測試結果的時候,覺得這個實現很不錯,沒有深入研究其實現。現在想把這個用到自己的一個項目中來,在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下面。