PlanetEngine中的内存管理
PlanetEngine中内存管理是比较传统的,主要就是这些:重载new、delete、引用计数、智能指针。
#ifndef EP3DCORE_MEM_MANAGE
#define EP3DNew new
#define EP3DDelete delete
#else
class EP3DEXPORT Memory
{
public:
static void* Allocate(size_t uiSize, char* acFile, uint uiLine, bool bIsArray);
static void Deallocate(char* pvAddr, bool bIsArray);
…
}
#define EP3DNew new(__FILE__, __LINE__)
#define EP3DDelete delete
void* operator new (size_t uiSize);
void* operator new[] (size_t uiSize);
void* operator new (size_t uiSize, char* acFile, unsigned int uiLine);
void* operator new[] (size_t uiSize, char* acFile, unsigned int uiLine);
void operator delete (void* pvAddr);
void operator delete[] (void* pvAddr);
void operator delete (void* pvAddr, char* acFile, unsigned int uiLine);
void operator delete[] (void* pvAddr, char* acFile, unsigned int uiLine);
#endif
通过宏控制,我们可以启动或关闭内存管理,全局的几个动态内存管理函数通过class Memory来分配跟踪内存的分配,且用户只能通过EP3DNew、EP3DDelete来实现内存管理;当开发人员在Debug模式下开发时开启内存管理EP3DNew、EP3DDelete的内存分配将由class Memory实现,当发布release版本时EP3DNew、EP3DDelete将编译为标准版本的new和delete;
除此外,class Memory还提供了一些额外的内存监控变量用来监控内存的使用情况,方便开发中进行内存泄露检测和性能优化;
class EP3DEXPORT Memory
{
public:
static void* Allocate(size_t uiSize, char* acFile, uint uiLine, bool bIsArray);
static void Deallocate(char* pvAddr, bool bIsArray);
static size_t GetNumNewCalls() { return ms_uiNumNewCalls; }
static size_t GetNumDeletetCalls() { return ms_uiNumDeleteCalls; }
static size_t GetNumBlocks() { return ms_uiNumBlocks; }
static size_t GetNumBytes() { return ms_uiNumBytes; }
static size_t GetMaxAllocatedBytes() { return ms_uiMaxAllocatedBytes; }
static size_t GetMaxBlockSize() { return ms_uiMaxBlockSize; }
static size_t GetHistogram(int i) { return ms_auiHistogram[i]; }
//输出结果
static void GenerateReportToLogFile(const char* acFilename);
static void GenerateReportToDebugWindow();
private:
static size_t ms_uiNumNewCalls;
static size_t ms_uiNumDeleteCalls;
static size_t ms_uiNumBlocks;
static size_t ms_uiNumBytes;
static size_t ms_uiMaxAllocatedBytes;
static size_t ms_uiMaxBlockSize;
static size_t ms_auiHistogram[32];
…
}
在对象生命周期管理方面,是采用简单的引用计数来控制的,继承分支如下:
RefCounter就是基础的计数器类,大部分对象从此类派生,简单易用可靠,但是缺点是明显的就是通用性比较差,同时对象大量被使用时,过多引用计数将带来一些性能损耗。
从RefCounter派生的类同时还具有一些使用上的限制:
1)派生类不应该在栈上创建对象,因为栈对象的生命周期是由c++来管理的(它们会在离开当前作用域时被销毁,从而绕过引用计数管理机制);
2)派生类不能进行拷贝,这样会造成引用计数器机制混乱;
3)派生类必须要有一虚析构函数;
4)派生类只有一个默认的构造函数;
class EP3DEXPORT RefCounter
{
public:
RefCounter(void);
virtual ~RefCounter(void);
uint AddRef(void);
uint Release(void);
uint GetReferenceCount(void);
uint GetTotalReferenceCount(void);
private:
uint m_uiRefCount;
static uint ms_uiObjects;
};
PlanetEngine使用一个模板智能指针来处理RefCounter对象的生命周期;Pointer<>隐藏了引用计数的实现细节,且应该一直指向RefCounter的派生对象;使用Poiner<>的好处:
1)访问一个空指针时,会给断言;
2)不需要手动调用AddRef()和Release();
3)可以在STL容器类里很好的工作,你不需要考虑手动去释放对象,智能指针会自动管理这一切.
4)不用考虑指针的所属,因为这一切智能指针为你管理了;
当然缺点也是明显的:
1)引用和解除引用会带来计数器的运算,解除引用时还会带来断言的检查,这会带来一些微不足道的性能损耗;
2)因为只有当计数为0时才释放对象,这样会带来对象存在超出它的预订时间,容易带来BUG,不过在程序退出时,未被释放的对象抛出断言会提醒你。
给出实现:
template<class T>
class Pointer
{
public:
Pointer(T* pObject = (T*)0)
{
m_pObject = pObject;
if (m_pObject)
m_pObject->AddRef();
}
Pointer(const Pointer<T>& ptr)
{
m_pObject = ptr.m_pObject;
if (m_pObject)
m_pObject->AddRef();
}
~Pointer()
{
if (m_pObject)
m_pObject->Release();
}
operator T* () const
{
LogAssert(m_pObject);
return m_pObject;
}
T& operator * () const
{
LogAssert(m_pObject);
return *m_pObject;
}
T* operator -> () const
{
LogAssert(m_pObject);
return m_pObject;
}
Pointer& operator = (const Pointer<T>& ptr)
{
if (m_pObject != ptr.m_pObject)
{
if (m_pObject)
m_pObject->Release();
m_pObject = ptr.m_pObject;
if (m_pObject)
m_pObject->AddRef();
}
return *this;
}
Pointer& operator = (T* pObject)
{
if (m_pObject != pObject)
{
if (m_pObject)
m_pObject->Release();
m_pObject = pObject;
if (m_pObject)
m_pObject->AddRef();
}
return *this;
}
bool operator == (T* pObject) const
{
return (m_pObject == pObject);
}
bool operator != (T* pObject) const
{
return (m_pObject != pObject);
}
bool operator == (const Pointer<T>& ptr) const
{
return (m_pObject == ptr.m_pObject);
}
bool operator != (const Pointer<T>& ptr) const
{
return (m_pObject != ptr.m_pObject);
}
protected:
T* m_pObject;
};
#define SmartPointer(classname) \
class classname; \
typedef Pointer<classname> classname##Ptr;