参考链接:
首先我们需要明确,内存碎片的目的到底是什么? 首先你要知道的是, 我们每次使用new T来初始化类型T的时候, 其实都发生以下两步操作:
内存分配,这一步使用的其实是operator new(也可以认为就是C语言中的malloc),这一步是直接和操作系统打交道的,操作系统需要经过相对繁琐的过程才能将一块指向空闲内存的指针返回给用户,所以这也是new比较耗时的一部分。
malloc会用到brk和mmap这两个系统调用来获取内存。
brk通过增加program break location(brk)地址来获取内存。
mmap在memory mapping segment中创建一块内存返回给malloc。
构造函数初始化内存,既然内存分配耗时,那我们很容易想到的就是一次性分配一大块内存,然后在用户需要的时候再划分其中一部分给用户,这样的话,一次分配, 多次使用, 在特定的需求场景下是可以较大的提高效率的(比如libuv中频繁的申请和释放内存),而用来管理这所谓的一大块内存的数据结构, 也就是今天我们要说的内存池。
另外一个好处在于, 频繁地使用new将导致系统内存空间碎片化,容易导致的后果就是很难找到一块连续的大块内存,造成内存碎片(非连续),空间利用率低。
内存碎片分为内碎片和外碎片两部分。内碎片是指分配给作业的存储空间中未被利用的部分;外碎片是指系统中无法利用的小存储块。
(以下图片来源:https://blog.csdn.net/wangbuji/article/details/123393611)

TIPS:
目前操作系统普遍采用的段页式内存分配方式就是将进程的内存区域分为不同的段,然后将每一段由多个固定大小的页组成。通过页表机制,使段内的页可以不必连续处于同一内存区域,从而减少了外部碎片,然而同一页内仍然可能存在少量的内部碎片,只是一页的内存空间本就较小,从而使可能存在的内部碎片也较少。
(以下图片资源来源:百度百科。红色字体及线为博主所画)

下面是一个简单的内存池思想的版本,实现的思想如下:
SimpleMemoryPool.hpp
#pragma once
namespace U
{
template<int ObjectSize, int NumOfObjects = 20>
class SimpleMemoryPool
{
private:
struct MemoryNode
{
char data[ObjectSize];
MemoryNode* pNext;
};
MemoryNode* freeMemoryNodeList;
MemoryNode* pBack;//用于指向被释放动态申请的数组空间的初始位置,对于动态申请的数组空间,只能释放其初始位置
private:
MemoryNode* _InitMemoryNodeList()
{
MemoryNode* pNode = new MemoryNode[NumOfObjects];
int count = 0;
while (count < NumOfObjects - 1)
{
pNode[count].pNext = &pNode[count + 1];
count++;
}
pNode[NumOfObjects - 1].pNext = nullptr;
return pNode;
}
public:
SimpleMemoryPool()
{
this->freeMemoryNodeList = _InitMemoryNodeList();
pBack = this->freeMemoryNodeList;
}
~SimpleMemoryPool()
{
std::cout << "调用SimpleMemoryPool" << std::endl;
if (this->pBack != nullptr)
delete[] this->pBack;
}
void* Malloc()
{
if (nullptr == this->freeMemoryNodeList)
{
//重新分配一段内存空间
std::cout << "重新添加内存空间" << std::endl;
MemoryNode* pNode = _InitMemoryNodeList();
//指向新的内容空间
freeMemoryNodeList = pNode;
pBack = pNode;
}
std::cout << "分配一个空间" << std::endl;
MemoryNode* pRet = freeMemoryNodeList;
freeMemoryNodeList = freeMemoryNodeList->pNext;
pRet->pNext = nullptr;
return pRet;
}
void Free(void* node)
{
std::cout << "释放一个空间" << std::endl;
MemoryNode* pNode = (MemoryNode*)node;
pNode->pNext = this->freeMemoryNodeList;//将需要释放的节点插入空闲节点头部
this->freeMemoryNodeList = pNode;
}
};
}
main.cpp
class TestClass
{
private:
static int count;
int curIndex;
public:
TestClass()
{
curIndex = count++;
}
~TestClass() {}
void Print()
{
std::cout << this << ":";
std::cout << "the " << curIndex << " object" << std::endl;
}
void* operator new(size_t size);
void operator delete(void* p);
void* operator new[](size_t size);
void operator delete[](void* p, size_t size);
};
int TestClass::count = 0;
U::SimpleMemoryPool<4, 5> mp;
void* TestClass::operator new(size_t size)
{
std::cout << "size:" << size << std::endl;
return mp.Malloc();
}
void TestClass::operator delete(void* p)
{
std::cout << "delete:" << p << std::endl;
mp.Free(p);
}
void* TestClass::operator new[](size_t size)
{
//size会比实际空间多4或8个字节,作用为记录数组长度以及内存对齐(至于具体是四个字节还是八个字节区别在于32位系统还是64位系统)
std::cout << "size:" << size << std::endl;
std::cout << "自定义的内存池不支持申请多个空间,所有默认调用系统malloc" << std::endl;
return malloc(size);
}
void TestClass::operator delete[](void* p, size_t size)
{
std::cout << "调用默认的free" << std::endl;
std::cout << "delete [] size : " << size << std::endl;
free(p);
}
int main(int argc, char* argv[])
{
for (int i = 0; i < 3; i++)
{
TestClass* p = new TestClass;
p->Print();
}
TestClass* p1 = nullptr;//3
p1 = new TestClass;
p1->Print();
TestClass* p2 = new TestClass;
p2->Print();
//delete p1;
TestClass* p3 = new TestClass;
p3->Print();
//delete p3;
TestClass* p4 = new TestClass[5];
delete[]p4;
system("pause");
return 0;
}
