栈(放指针)+对象池
重载了具体对象的operator new
缺点:没有用数组,还是存在malloc的空隙.
在一个长时间运行的系统中(如一个httpserver),内存的分配和释放操作很频繁. 如果只是按照平常的编程模式, 在需要内存时在堆中分配,在使用完后立即释放, 效率会很低. 因为在c++中,系统默认的new和delete操作为了兼顾所有的情况,效率很低,同时,反复的内存分配和释放,肯定会导致“内存空洞”的产生,而导致整个程序的运行性能的下降。所以,有必要事先分配若干内存,在使用时直接使用,而不用现分配,这样程序的效率会大大提高。
这里我写出了一个简单内存池的实现方案。虽然比较小,但还是比较实用而且效率也比较高。为了实现兼容性,我以模板的方式来实现。
首先我先谈谈内存池的管理策略。管理内存池,我选择的方式是以数组的方式来管理。因为的数组的随机访问的时间度为O(1),访问效率非常的高。也有用链表的方式来实现内存管理的,这样灵活性比较高,但效率比较低,所以我觉得用数组的方式比较好。
下面谈谈实现的细节。假设我们为某个对象A建立一个指针数组 A* P[100](这里为了简单起见,以一个静态数组来描述),然后为数组中的元素分配空间:
for(int i=0; i<100; i++)
P[i] = ::operator new(sizeof(A));
这样我们为对象A实现一个大小为100的内存池。下面讲讲如何使用:
建立一个索引变量 int index ,来指向下一个未使用的内存空间。初始化时index为0。当我们需要内存时,将P[index]返回,同时将P[index]=NULL,表示该块内存已经使用,同时将index+1,指向内存数组中的下一个元素.当归还时,将index-1,同时把P[index]指向归还的内存块.
下面给出具体的实现.
#i nclude <vector> //我们用vector 容器来实现数组的动态增长
using namespace sgd;
template<class T>
class CMemPool
{
public:
CMemPool(DWORD Block_size , int Alloc_size) //确定内存池的初始化大小以及增长的大小
:_BLOCK_SIZE(Block_size),
_ALLOC_SIZE(Alloc_size),
_BlockIndex(-1)
{
InitializeCriticalSection(&_cs); //为了线程安全,在分配时需要进行临界处理
_alloc_blocks();
};
CMemPool2()
:_BLOCK_SIZE(100),
_ALLOC_SIZE(10),
_BlockIndex(-1)
{
InitializeCriticalSection(&_cs);
_alloc_blocks();
};
~CMemPool2()
{
for( DWORD i=0 ; i<_BLOCK_SIZE; i++ )
{
if( _pMemBlock[i] )
{
try
{
::operator delete(_pMemBlock[i]); //释放内存
}
catch(...)
{
}
}
}
DeleteCriticalSection(&_cs);
}
void* _alloc(size_t size); //请求内存
void _free(void* pobj , size_t size ); //归还内存
private:
void _alloc_blocks(); //创建内存块
void _realloc_blocks(); //内存块不足时重新创建
private:
CRITICAL_SECTION _cs;
vector<T*> _Blocks;
DWORD _BlockIndex; //块索引
DWORD _BLOCK_SIZE;
const int _ALLOC_SIZE;
};
template<class T>
void CMemPool<T>:: _alloc_blocks()
{
for( DWORD i=0; i<_BLOCK_SIZE; i++ )
{
T* p = static_cast<T*>(::operator new(sizeof(T)));
_Blocks.push_back(p);
}
_BlockIndex = 0;
}
template<class T>
void CMemPool<T>::_realloc_blocks()
{
for( DWORD i= _BLOCK_SIZE; i<size; i++ )
{
T* p = static_cast<T*>(::operator new(sizeof(T)));
_Blocks.push_back(p);
}
_BLOCK_SIZE = size;
}
template<class T>
void* CMemPool<T>::_alloc(size_t size)
{
if( size != sizeof(T) )
return ::operator new(size); //这个很重要,当要分配的内存与实际对象的大小不符时,应该调用
//系统默认的分配操作,否则将会出现不可预料的错误,具体可以参
//考《Effective C++》
EnterCriticalSection(&_cs);
if( _BlockIndex >= _BLOCK_SIZE ) //已分配的内存块不够,从新分配
_realloc_blocks();
T* p = _Blocks[_BlockIndex];
_Blocks[_BlockIndex] = NULL; //将指针赋值为NULL,表示此块内存已经被使用
_BlockIndex ++ ;
LeaveCriticalSection(&_cs);
return p;
}
template<class T>
void CMemPool<T>::_free(void* pobj , size_t size )
{
if( pobj == NULL )
return ;
if( size != sizeof(T) ) //原因同上
{
::operator delete(pobj);
}
else if( _BlockIndex<= 0 ) //要释放的内存不是我们内存池的哦!
{
::operator delete(pobj);
}
else
{
EnterCriticalSection(&_cs);
_Blocks[_BlockIndex-1] = static_cast<T*>(pobj); //将指针指向这块归还的内存
_BlockIndex --;
LeaveCriticalSection(&_cs);
}
}
下面是具体的使用。对于每个是内存池的对象来讲,必须重载该对象的 new ,delete操作符.
class M;
class M
{
public:
M(){};
virtual ~M(){}; //析构函数必须申明成虚拟函数
static void* operator new(size_t size);
static void operator delete(void* pobj, size_t size);
}
CMemPool<M> _MemPool;
....... XXX.cpp实现
CMemPool<M> _MemPool(100,10); //初始化100块内存,增长速度为10
void* M::operator new(size_t size)
{
return _MemPool._alloc(size);
}
void M::operator delete(void* pobj,size_t size)
{
_MemPool._free(pobj,size);
}
上面的代码实现了一个比较实用的内存池模板类,完全可以运用到实际的项目中。当然,这个类在分配内存时对内存分配失败的处理还得有作进一步处理。