实现针对类的内存池
一、内存池的概念和实现原理概述
malloc:内存浪费,频繁分配小块内存,则浪费更加显得明显。
“内存池”,要解决什么问题?
(1)减少malloc的次数,减少malloc()调用次数就意味着减少对内存的浪费;
(2)减少malloc的调用次数,是否能够提高程序运行效率? 会有一些速度和效率上的提升,但是提升不明显。
“内存池”的实现原理
用malloc申请一大块内存,当你要分配的时候,我从这一大块内存中一点一点的分配给你,
当一大块内存分配的差不多的时候,我再用malloc再申请一大块内存,然后再一点一点的分配给你。
减少内存浪费,提高运行效率!
二、针对一个类的内存池的实现代码
针对一个类 A 的内存池 :
A *pa = new A();
delete pa;
用内存池的手段实现new,delete一个对象。
代码实现:
#include <iostream>
using namespace std;
#include <ctime>
//#define MYMEMPOOL 1
class A{
public:
static void* operator new(size_t size);
static void operator delete(void* phead);
static int m_iCount; // 分配计数,每new一次就统计一次
static int m_iMallocCount; // 每malloc一次就统计一次
private:
A* next;
static A* m_FreePos; // 总是指向一块可以分配出去的内存的首地址
static int m_sTrunkCount; // 一次分配多少倍的该类内存
};
int A::m_iCount = 0;
int A::m_iMallocCount = 0;
A* A::m_FreePos = nullptr;
int A::m_sTrunkCount = 5; // 一次分配5倍的该类内存作为内存池的大小
void* A::operator new(size_t size){
#ifdef MYMEMPOOL
A* ppoint = (A*)malloc(size);
return ppoint;
#endif
A* tmplink;
if(m_FreePos == nullptr){
size_t realsize = m_sTrunkCount * size;
m_FreePos = reinterpret_cast<A*>(new char[realsize]);
tmplink = m_FreePos;
for(; tmplink != &m_FreePos[m_sTrunkCount - 1]; ++tmplink){
tmplink->next = tmplink + 1;
}
tmplink->next = nullptr;
++m_iMallocCount;
}
tmplink = m_FreePos;
m_FreePos = m_FreePos->next;
++m_iCount;
return tmplink;
}
void A::operator delete(void* phead){
#ifdef MYMEMPOOL
free(phead);
return;
#endif
(static_cast<A*>(phead))->next = m_FreePos;
m_FreePos = static_cast<A*>(phead);
}
void test(){
clock_t start, end; //包含头文件 #include <ctime>
start = clock();
//for (int i = 0; i < 500'0000; i++)
for (int i = 0; i < 15; i++)
{
A *pa = new A();
printf("%p\n", pa);
}
end = clock();
cout << "申请分配内存的次数为:" << A::m_iCout
<< " 实际malloc的次数为:" << A::m_iMallocCount
<< " 用时(毫秒): " << end - start << endl;
}
int main(){
test();
return 1;
}
三、内存池代码的改进
单独为内存池技术写一个内存池类
嵌入式指针(embedded pointer)
(1) 嵌入式指针概念
一般应用在内存池相关的代码中;
成功使用嵌入式指针有个前提条件:(类A对象的sizeof必须不小于4字节)。
(2) 嵌入式指针工作原理
借用A对象所占用的内存空间中的前4个字节,这4个字节用来 链住这些空闲的内存块【sizeof() 超过4字节的类就可以安全的使用嵌入式指针,因为在vs2017环境下,指针的sizeof值是4】;
但是,一旦某一块被分配出去,那么这个块的 前4个字节 就不再需要,此时这4个字节可以被正常使用。
(3) 代码实现
class TestEP{
public:
int m_i;
int m_j;
public:
struct obj{
struct obj* next;
};
};
void func()
{
TestEP mytest;
cout << sizeof(mytest) << endl; //8
TestEP::obj* ptemp; //定义一个指针
ptemp = (TestEP::obj*)&mytest; //把对象mytest首地址给了这个指针ptemp,这个指针ptemp指向对象mytest首地址;
cout << sizeof(ptemp->next) << endl; //4
cout << sizeof(TestEP::obj) << endl; //4
ptemp->next = nullptr;
}
专门的内存池类
class myallocator{
public:
// 分配内存接口
void* allocate(size_t size){
obj* tmplink;
if(m_FreePos == nullptr){
size_t realsize = m_sTrunkCount * size;
m_FreePos = (obj*)malloc(realsize);
tmplink = m_FreePos;
for(int i = 0; i < m_sTrunkCount - 1; ++i){
tmplink->next = (obj*)((char*)tmplink + size);
tmplink = tmplink->next;
}
tmplink->next = nullptr;
}
tmplink = m_FreePos;
m_FreePos = m_FreePos->next;
return tmplink;
}
// 释放内存接口
void deallocate(void* phead){
((obj*)phead)->next = m_FreePos;
m_FreePos = (obj*)phead;
}
private:
// 写在类内的结构,这样只让其在类内使用
struct obj{
struct obj* next;
};
int m_sTrunkCount = 5;
obj* m_FreePos = nullptr;
};
使用实例
class A{
public:
static myallocator myalloc;
static void* operator new(size_t size){
return myalloc.allocate(size);
}
static void operator delete(void* phead){
return myalloc.deallocate(phead);
}
public:
// 为了保证sizeof(A)凑够4字节,定义了两个int成员变量;
int m_i;
int m_j;
};
myallocator A::myalloc;
// 测试
void test(){
A* mypa[100];
for(int i = 0; i < 15; ++i){
mypa[i] = new A();
mypa[i]->m_i = 12;
mypa[i]->m_j = 15;
printf("%p\n", mypa[i]);
}
for(int i = 0; i < 15; ++i){
delete mypa[i];
}
}