一、内存池概念
内存池是一种自主的内存管理机制。就是将我们的内存管理放在了应用程序端。
那么它的简单处理做了什么事呢?
首先,我们从堆上分配出一块很大的内存块。接着我们按照划分将其划分成每个不同的小组。这个每个小组存储一个数据块。针对于每个小组的组内来说就是一个简单的数据结构。这个数据结构我们将其分为两个部分,这两部分代表分别存储数据的数据域和找到下一个指针的指针域。
划分好之后我们用一个标志来标志它的起始位置。最后形成一个静态链表的组织方式,将第一个内存单元的指针域指向第二个内存单元。第二个内存单元指向下一个内存单元。如下图所示:
现在这个内存块管理的是未分配的内存单元。
二、通用内存池的实现
这个设计是将内存池和使用类单独分开来设计。我们是先设计一个内存池实现的类,然后再设计使用类。这个使用类的设计我们是这样的:如果这个使用类中有new,那么如果它通过operator new来处理的时候可以在operator new里面实现自主的内存管理。
那么我们实现的时候使用类是如何在内存池上开辟内存的呢?
这样的话这两个类就应该产生一个叫信息交互的类。也就是这两个类应该进行消息通讯。那么在C++中最简单的消息通讯就是产生接口。那么另一个类只要调用它的接口完成这个行为就可以了。所以在通用内存池的实现中我们只要让使用类去调用内存池的通用接口就可以了。
那么内存池应该提供哪些接口呢?
我们知道内存池的本质管理的是堆内存。那么内存的管理的第一件事就是alloc()开辟内存。第二件事就是dealloc()回收内存。
所以内存池对外就提供两个接口:
1、alloc();申请内存
2、dealloc();释放内存
如下图所示:
三、代码实现通用内存池
const int MEM_SIZE = 10;
template<typename T>
class MEM_POOL//内存池类
{
public:
MEM_POOL()
{
pool = NULL;
}
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 = ptr)
{
pCur->next = pCur + 1;
}
pCur->next = NULL;
}
void* rt = pool;
pool = pool->next;
return rt;
}
void* dealloc(void* ptr)
{
Node* nptr = (Node*)ptr;
if(ptr == NULL)
{
return;
}
nptr->pnext = pool;
pool = nptr;
}
private:
struct Node
{
public:
Node(T value = T())
:val(value), pnext(NULL)
{}
public:
T val;
Node* pnext;
};
Node* pool;
};
class Student
{
public:
Student(std::string name, std::string id, int age)
:mname(name), mid(id), mage(age)
{}
void* operator new(size_t size)
{
return mm.alloc(size);
}
void operator delete(void* ptr)
{
mm.dealloc(ptr);
}
private:
std::string mname;
std::string mid;
int mage;
static MEM_POOL<Student> mm;
};
MEM_POOL<Student> Student::mm;
int main()
{
Student* pstu1 = new Student("zhangsan", "001",20);
Student* pstu1 = new Student("lisi", "002",19);
delete pstu1;
delete pstu2;
return 0;
}
四、通用内存池的优化—单例模式实现
const int MEM_SIZE = 10;
template<typename T>
class MEM_POOL//内存池类
{
public:
static MEM_POOL<T>* getInstance()
{
if(ppm = NULL)
{
ppm = new MEM_POOL<T>();
}
return ppm;
}
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 = ptr)
{
pCur->next = pCur + 1;
}
pCur->next = NULL;
}
void* rt = pool;
pool = pool->next;
return rt;
}
void* dealloc(void* ptr)
{
Node* nptr = (Node*)ptr;
if(ptr == NULL)
{
return;
}
nptr->pnext = pool;
pool = nptr;
}
private:
MEM_POOL()
{
pool = NULL;
}
MEM_POOL(const MEM_POOL<T>&);
struct Node
{
public:
Node(T value = T())
:val(value), pnext(NULL)
{}
public:
T val;
Node* pnext;
};
Node* pool;
static MEM_POOL<T>* ppm;
};
template<typename T>
MEM_POOL<T>* MEM_POOL<T>::ppm = NULL;
class Student
{
public:
Student(std::string name, std::string id, int age)
:mname(name), mid(id), mage(age)
{}
void* operator new(size_t size)
{
return pmm->alloc(size);
}
void operator delete(void* ptr)
{
pmm->dealloc(ptr);
}
private:
std::string mname;
std::string mid;
int mage;
static MEM_POOL<Student> pmm;
};
MEM_POOL<Student>* Student::pmm = MEM_POOL<Student>::getInstance();
int main()
{
Student* pstu1 = new Student("zhangsan", "001",20);
Student* pstu1 = new Student("lisi", "002",19);
delete pstu1;
delete pstu2;
return 0;
}
五、内存池的优点
C/C++下分配足够的内存、追踪内存的分配、在不需要的时候释放内存—这是相当复杂。而直接使用系统调用malloc/free、new/delete进行内存分配和释放,有以下弊端:
- 调用malloc/new,系统需要根据“最先匹配”、“最优匹配”或其他算法在内存空闲块表中查找一块空闲内存,调用free/delete,系统可能需要合并空闲内存块,这些会产生额外开销
- 频繁使用时会产生大量内存碎片,从而降低程序运行效率
- 容易造成内存泄漏
- 内存池是代替直接调用malloc/free、new/delete进行内存管理的常用方法,当我们申请内存空间时,首先到我们的内存池中查找合适的内存块,而不是直接向操作系统申请,优点在于:
- 比malloc/free进行内存申请/释放的方式快
- 不会产生或很少产生堆碎片
- 可避免内存泄漏