• C++ 内存池


    C++ 内存池

    参考链接:

    1.https://blog.csdn.net/wangbuji/article/details/123393611

    2.https://www.jianshu.com/p/bbba7e72fa59

    内存碎片

      首先我们需要明确,内存碎片的目的到底是什么? 首先你要知道的是, 我们每次使用new T来初始化类型T的时候, 其实都发生以下两步操作:

    1. 内存分配,这一步使用的其实是operator new(也可以认为就是C语言中的malloc),这一步是直接和操作系统打交道的,操作系统需要经过相对繁琐的过程才能将一块指向空闲内存的指针返回给用户,所以这也是new比较耗时的一部分。

      malloc会用到brk和mmap这两个系统调用来获取内存。

      brk通过增加program break location(brk)地址来获取内存。

      mmap在memory mapping segment中创建一块内存返回给malloc。

    2. 构造函数初始化内存,既然内存分配耗时,那我们很容易想到的就是一次性分配一大块内存,然后在用户需要的时候再划分其中一部分给用户,这样的话,一次分配, 多次使用, 在特定的需求场景下是可以较大的提高效率的(比如libuv中频繁的申请和释放内存),而用来管理这所谓的一大块内存的数据结构, 也就是今天我们要说的内存池。

      另外一个好处在于, 频繁地使用new将导致系统内存空间碎片化,容易导致的后果就是很难找到一块连续的大块内存,造成内存碎片(非连续),空间利用率低。

      内存碎片分为内碎片外碎片两部分。内碎片是指分配给作业的存储空间中未被利用的部分;外碎片是指系统中无法利用的小存储块。

    (以下图片来源:https://blog.csdn.net/wangbuji/article/details/123393611

    效果图

    TIPS:

    目前操作系统普遍采用的段页式内存分配方式就是将进程的内存区域分为不同的段,然后将每一段由多个固定大小的页组成。通过页表机制,使段内可以不必连续处于同一内存区域,从而减少了外部碎片,然而同一页内仍然可能存在少量的内部碎片,只是一页的内存空间本就较小,从而使可能存在的内部碎片也较少。

    (以下图片资源来源:百度百科。红色字体及线为博主所画)

    效果图

    基本思路

    下面是一个简单的内存池思想的版本,实现的思想如下:

    1. 通过维护一个freeMemoryNodeList链表。当要申请空间的时候,就从链表上面摘下一个结点;要释放一个空间的时候,就从将释放的结点重新插入到链表里面。
    2. 每个结点的大小是提前通过template传入的,就是要分配的对象的大小。

    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;
    		}
    
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    效果图

  • 相关阅读:
    智源林咏华:大模型的竞争,差距核心在数据 | AGI 技术 50 人
    了解MQ和安装使用RabbitMQ
    虚拟机扩容
    POJ 1222 EXTENDED LIGHTS OUT 反转+点灯游戏
    iText7画发票PDF——小tips
    决策树-分析与应用
    【C++项目】boost搜索引擎项目
    vue学习-07TodoList本地存储以及TodoList的自定义事件
    阿里云部署开源MQTT平台mosquitto的docker操作
    光纤的初步认识
  • 原文地址:https://blog.csdn.net/qq135595696/article/details/126186289