默认内存管理的不足:
malloc/free 和 new/delete 在堆上申请和释放内存都有一定的额外开销
。开销来自维护 内存空闲块表
。malloc和new 申请堆内存时,首先查找内部维护的内存空闲块表,并且需要根据一定的算法找到合适大小的空闲内存块
。如果该空闲内存块过大,还需要切割成已分配的部分和较小的空闲块。然后系统更新内存空闲块表
,完成一次内存分配。类似地,在free和delete释放内存时,系统把释放的内存块重新加入到空闲内存块表中。如果有可能的话,可以把相邻的空闲块合并成较大的空闲块。默认的内存管理函数还考虑到多线程的应用
,需要在每次分配和释放内存时加锁
,同样增加了开销。可见,如果应用程序频繁地在堆上分配和释放内存
,则会导致性能的损失
。并且会使系统中出现大量的内存碎片
,降低内存的利用率
。默认的分配和释放内存算法自然也考虑了性能,然而这些内存管理算法的通用版本为了应付更复杂、更广泛的情况,需要做更多的额外工作。而对于某一个具体的应用程序来说,适合自身特定的内存分配释放模式的自定义内存池则可以获得更好的性能
。
内存池的定义:
应用程序可以通过调用系统的内存分配函数预先一次性申请适当大小的内存作为一个内存池
,并为这个内存池类或结构体
定义一些分配和释放内存块的成员函数
。之后应用程序自己对内存的分配和释放则可以通过这个内存池类及其成员函数
来完成。只有当内存池大小需要动态扩展时
,才需要再调用系统的内存分配函数,其他时间对内存的一切操作都在应用程序的掌控之中。
内存池的分类:
1、从线程安全角度分为单线程内存池和多线程内存池。单线程内存池整个生命周期只被一个线程使用,因而不需要考虑互斥
访问的问题。多线程内存池有可能被多个线程共享,因此则需要在每次分配和释放内存时加锁
。相对而言,单线程内存池性能更高
,而多线程内存池适用范围更广
。
2、从内存池可分配内存单元大小来分为固定内存池和可变内存池。固定内存池是指应用程序每次从内存池中分配出来的内存单元大小事先已经确定,是固定不变的
;维护起来方便,性能更高。而可变内存池则每次从内存池中分配出来的内存单元大小可以按需变化
,应用范围更广,而性能比固定内存池要低。
内存池优点:
针对特殊情况,例如需要频繁
分配释放固定大小的内存对象
时,不需要复杂的分配算法和多线程/多进程保护(系统内存管理一直会加锁解锁)。也不需要维护内存空闲表的额外开销,只需维护简单的内存池块头信息,从而获得较高的性能。由于开辟一定数量的连续内存空间
作为内存池块,因而一定程度上提高了程序局部性和数据访问的速度,提升了程序性能。比较容易控制页边界对齐和内存字节对齐,没有内存碎片的问题
。
new:分为两步
1.operator new
2.调用构造
delete:分为两步
1.释放其他资源
2.operator delete
const int MEM_SIZE = 10;
template<typename T>
class MEM_Pool
{
public:
static MEM_Pool<T>* getIntance()
{
if (psing == NULL)
{
psing = new MEM_Pool<T>();
}
return psing;
}
void* alloc(size_t size) //分配空间
{
if (pool == NULL)
{
pool = (Node*)new char[(size + 4)*MEM_SIZE]();
Node* pCur = pool;
for (pCur; pCur < pool + MEM_SIZE - 1; pCur = pCur + 1)
{
pCur->pnext = pCur + 1;
}
pCur->pnext = NULL;
}
void* rt = pool;
pool = pool->pnext;
return rt;
}
void dealloc(void* ptr) //释放空间
{
if (ptr == NULL)
return;
Node* pptr = (Node*)ptr;
pptr->pnext = pool;
pool = pptr;
}
private:
MEM_Pool()
{
pool = NULL;
}
MEM_Pool(const MEM_Pool<T>&);
class Node
{
public:
Node(T val = T()) :mdata(val), pnext(NULL){}
public:
T mdata;
Node* pnext;
};
Node* pool;
static MEM_Pool<T>* psing;
};
template<typename T>
MEM_Pool<T>* MEM_Pool<T>::psing = NULL;
class CGoods
{
public:
CGoods(std::string name, float price, int amount)
:mname(name), mprice(price), mamount(amount)
{}
void* operator new(size_t size)
{
return pmm->alloc(size);
}
void operator delete(void* ptr)
{
pmm->dealloc(ptr);
}
private:
std::string mname;
float mprice;
int mamount;
static MEM_Pool<CGoods>* pmm;
};
MEM_Pool<CGoods>* CGoods::pmm = MEM_Pool<CGoods>::getIntance();
int main()
{
CGoods* pgood1 = new CGoods("A", 4.5, 100);
CGoods* pgood2 = new CGoods("B", 2.0, 200);
delete pgood1;
return 0;
}