对象池和内存池很相似,相辅相成。对象池的内部依然使用内存池申请内存,如果对象池较小,在内存池某个内存块上申请该内存池,进行更好的内存划分。某个对象需要对象池,那么就直接继承所写的对象池,很方便的拆卸。避免对象的高频申请和释放
#ifndef _CELLObjectPool_hpp_
#define _CELLObjectPool_hpp_
#include<stdlib.h>
#include<assert.h>
#include<mutex>
#ifdef _DEBUG
#ifndef xPrintf
#include<stdio.h>
#define xPrintf(...) printf(__VA_ARGS__)
#endif
#else
#ifndef xPrintf
#define xPrintf(...)
#endif
#endif // _DEBUG
template<class Type, size_t nPoolSzie>
class CELLObjectPool
{
public:
CELLObjectPool()
{
initPool();
}
~CELLObjectPool()
{
if(_pBuf)
delete[] _pBuf;
}
private:
class NodeHeader
{
public:
//下一块位置
NodeHeader* pNext;
//内存块编号
int nID;
//引用次数
char nRef;
//是否在内存池中
bool bPool;
private:
//预留
char c1;
char c2;
};
public:
//释放对象内存
void freeObjMemory(void* pMem)
{
NodeHeader* pBlock = (NodeHeader*)((char*)pMem - sizeof(NodeHeader));
xPrintf("freeObjMemory: %llx, id=%d\n", pBlock, pBlock->nID);
assert(1 == pBlock->nRef);
if (pBlock->bPool)
{
std::lock_guard<std::mutex> lg(_mutex);
if (--pBlock->nRef != 0)
{
return;
}
pBlock->pNext = _pHeader;
_pHeader = pBlock;
}
else {
if (--pBlock->nRef != 0)
{
return;
}
delete[] pBlock;
}
}
//申请对象内存
void* allocObjMemory(size_t nSize)
{
std::lock_guard<std::mutex> lg(_mutex);
NodeHeader* pReturn = nullptr;
if (nullptr == _pHeader)
{
pReturn = (NodeHeader*)new char[sizeof(Type) + sizeof(NodeHeader)];
pReturn->bPool = false;
pReturn->nID = -1;
pReturn->nRef = 1;
pReturn->pNext = nullptr;
}
else {
pReturn = _pHeader;
_pHeader = _pHeader->pNext;
assert(0 == pReturn->nRef);
pReturn->nRef = 1;
}
xPrintf("allocObjMemory: %llx, id=%d, size=%d\n", pReturn, pReturn->nID, nSize);
return ((char*)pReturn + sizeof(NodeHeader));
}
private:
//初始化对象池
void initPool()
{
//断言
assert(nullptr == _pBuf);
if (_pBuf)
return;
//计算对象池的大小
size_t realSzie = sizeof(Type) + sizeof(NodeHeader);
size_t n = nPoolSzie*realSzie;
//申请池的内存
_pBuf = new char[n];
//初始化内存池
_pHeader = (NodeHeader*)_pBuf;
_pHeader->bPool = true;
_pHeader->nID = 0;
_pHeader->nRef = 0;
_pHeader->pNext = nullptr;
//遍历内存块进行初始化
NodeHeader* pTemp1 = _pHeader;
for (size_t n = 1; n < nPoolSzie; n++)
{
NodeHeader* pTemp2 = (NodeHeader*)(_pBuf + (n* realSzie));
pTemp2->bPool = true;
pTemp2->nID = n;
pTemp2->nRef = 0;
pTemp2->pNext = nullptr;
pTemp1->pNext = pTemp2;
pTemp1 = pTemp2;
}
}
private:
//
NodeHeader* _pHeader;
//对象池内存缓存区地址
char* _pBuf;
//
std::mutex _mutex;
};
template<class Type, size_t nPoolSzie>
class ObjectPoolBase
{
public:
void* operator new(size_t nSize)
{
return objectPool().allocObjMemory(nSize);
}
void operator delete(void* p)
{
objectPool().freeObjMemory(p);
}
template<typename ...Args>
static Type* createObject(Args ... args)
{ //不定参数 可变参数
Type* obj = new Type(args...);
//可以做点想做的事情
return obj;
}
static void destroyObject(Type* obj)
{
delete obj;
}
private:
//
typedef CELLObjectPool<Type, nPoolSzie> ClassTypePool;
//
static ClassTypePool& objectPool()
{ //静态CELLObjectPool对象
static ClassTypePool sPool; //在多线程时可能不安全
return sPool;
}
};
#endif // !_CELLObjectPool_hpp_
设置对象池的原因主要是为了进一步优化性能和资源管理。内存池主要解决的是内存分配效率和碎片化问题,而对象池则针对对象的创建和销毁进行优化。以下是设置对象池的几个主要原因:
- 减少对象创建和销毁的开销:对象的创建和销毁通常比内存分配和释放更加耗时,因为它们涉及到构造函数和析构函数的调用。对象池通过重用已经创建的对象来减少这些开销1。
- 提高响应速度:在需要频繁创建和销毁同类型对象的场景中,对象池可以显著提高程序的响应速度。因为从对象池中获取一个已经初始化的对象通常比创建一个新对象要快得多1。
- 更好的资源管理:对象池允许更精细的控制对象的生命周期和数量,有助于避免资源泄漏和过度消耗资源1。
- 提高稳定性:在高负载情况下,对象池可以保证有一定数量的对象可用,避免在资源紧张时无法创建新对象的问题1。
- 减少垃圾回收的频率:对于使用垃圾回收机制的编程语言,频繁的对象创建和销毁会触发垃圾回收,这可能会导致性能问题。对象池通过重用对象来减少垃圾回收的需要2。
总的来说,对象池是对内存池的补充,它们共同提供了一种更高效、更稳定的资源管理方式。在设计高性能的服务器引擎时,合理使用内存池和对象池可以显著提升性能和资源利用率。
并且内存池管理和对象池管理可以通过日志在内存细节上把控