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