为什仫要使用内存池?
1.通常我们用new/delete和malloc/free来管理内存,可能会需要频繁的调用内存,减少运行时间,增加效率.
2.避免内存碎片
传统的new/delete的弊端>
1.分配内存时要查看空闲分区表,根据一定的算法来分配,比如最佳适应算法,最差适应算法...,然后更新空闲分区表.释放的时候,也需要在空闲分区表中加入该释放的内存,如果上下也是空闲的还需要将内存碎片合并为一个大的空闲分区 .
2.默认的内存管理函数还考虑了多线程的应用,增加了开销.
3.频繁的申请和释放还将导致内存碎片.
内存池是在真正使用内存之前,先申请一个大的内存块.当需要使用内存时,就从内存池中分配一块使用,当使用完了不用还给操作系统而是还给内存池。如果内存池不够就向内存申请一块更大的内存块.
存在内存池的好处?
1.因为申请的内存都比较大,所以降低了外部碎片(内存碎片).
2.因为是一次性申请一块较大的内存,避免了频繁的向内存的请求操作,提高了内存的分配效率,避免内存泄漏.
内碎片
分配内存时的大小比需求的内存大,有部分内存未使用,这块未使用的内存就称为内碎片
外碎片/内存碎片
当空闲的存储器的和计起来足够满足一个分配请求,但是没有一个单独的空闲块足够大可以处理这个请求
下面介绍一种高效的内存池,它的实现类似链表,链表上的每个结点都是一个对象池,如果申请对象则去内存池中申请,归还的时候还给内存池即可.
#pragma once
#include<vector>
#include<string>
template<class T>
class ObjectPool
{
struct BlockNode
{
void *_memory; //内存块
BlockNode *_next;
size_t _objNum; //内存对象的个数
BlockNode(size_t objNum)
:_objNum(objNum)
,_next(NULL)
{
_memory=malloc(_itemSize*objNum);
}
~BlockNode()
{
free(_memory);
}
};
protected:
size_t _countIn; //当前结点的计数
BlockNode *_first;
BlockNode *_last;
size_t _maxNum; //结点申请内存块对象的个数
static size_t _itemSize; //单个对象的大小
T *_lastDelete;
public:
ObjectPool(size_t initNum=32,size_t maxNum=1000000)
:_countIn(0)
,_maxNum(maxNum)
,_lastDelete(NULL)
{
_first=_last=new BlockNode(initNum);
}
~ObjectPool()
{
Destroy();
}
void Destroy()
{
BlockNode *cur=_first;
while (cur)
{
BlockNode *del=cur;
cur=cur->_next;
delete del;
}
_first=_last=NULL;
}
T* New()
{
if(_lastDelete)
{
//优先考虑以前释放的空间
T *obj=_lastDelete;
_lastDelete=*((T **)_lastDelete);
return new(obj)T;
}
if(_countIn >= _last->_objNum) //申请新的结点和对象
{
size_t size=2*_countIn;
if(size > _maxNum)
size=_maxNum;
_last->_next=new BlockNode(size);
_last=_last->_next;
_countIn=0;
}
T *obj=(T *)((char *)_last->_memory + _countIn*_itemSize);
_countIn++;
return new(obj)T();
}
void Delete(T *ptr)
{
if(ptr)
{
ptr->~T();
*((T **)ptr)=_lastDelete;
_lastDelete=ptr;
}
}
protected:
static size_t GetItemSize()
{
if(sizeof(T) > sizeof(T *))
return sizeof(T);
else
return sizeof(T *);
}
};
template<class T>
size_t ObjectPool<T>::_itemSize=ObjectPool<T>::GetItemSize();
over~~~