• 18.3 内存池概念、代码实现和详细分析


    一:内存池的概念和实现原理概述

    malloc:内存浪费,频繁分配小块内存,浪费更加明显。

    “内存池”要解决什么问题?

    1、减少malloc()的次数,减少malloc()调用次数就意味着减少对内存的浪费
    2、减少malloc()的调用次数,是否能够提高程序运行效率?
       会有一些速度和效率上的提升,但是提升不明显。

    “内存池”实现原理:

    1、使用malloc申请一个大块内存,当有分配需求的时候,从这一大块内存中一点点的进行分配,当这一大块内存分配快占满的时候了,再用malloc申请一大块内存,再一点点进行分配。
    2、减少内存浪费,提高运行效率。

    二:针对一个类的内存池实现演示代码

    针对一个类的内存池A,A* pa = new A(),delete pa; 用内存池的手段实现new、delete对象。

    1、内存池的operator new实现



    void* A::operator new(size_t size)
    {
    	A* ptmplink;
    	if (m_freePosition == nullptr)
    	{
    		//为空,要申请一大块内存
    		size_t realsize = m_sTrunkCount * size;  //申请m_sTrunkCount这么多倍的内存
    		m_freePosition = reinterpret_cast<A*>(new char[realsize]);  //传统new,调用系统底层的malloc
    		ptmplink = m_freePosition;
    
    		//把分配出来的这一大块内存(5下块),彼此要链起来,供后续使用
    		for (; ptmplink != &m_freePosition[m_sTrunkCount - 1]; ptmplink++)
    		{
    			ptmplink->next = ptmplink + 1;
    		}
    		ptmplink->next = nullptr;
    		++m_iMallocCount;
    	}
    	ptmplink = m_freePosition;
    	m_freePosition = m_freePosition->next;
    	++m_iCount;
    	return ptmplink;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2、内存池的operator delete实现


    在这里插入图片描述

    void A::operator delete(void* phead)
    {
    	(static_cast<A*>(phead))->next = m_freePosition;
    	m_freePosition = static_cast<A*>(phead);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3、完整代码

    #include
    #include 
    
    using namespace std;
    
    //#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_freePosition;  //总是指向一块可以分配出去的内存首地址
    	static int m_sTrunkCount;  //一次分配多少倍的该类内存
    };
    
    int A::m_iCount = 0;
    int A::m_iMallocCount = 0;
    A* A::m_freePosition = nullptr;
    int A::m_sTrunkCount = 5;  //一次分配5倍的该类内存作为内存池的大小
    
    void* A::operator new(size_t size)
    {
    #ifdef MYMEMPOOL
    	A* ppoint = (A*)malloc(size);
    	++m_iMallocCount;
    	++m_iCount;
    	return ppoint;
    #else
    	A* ptmplink;
    	if (m_freePosition == nullptr)
    	{
    		//为空,要申请一大块内存
    		size_t realsize = m_sTrunkCount * size;  //申请m_sTrunkCount这么多倍的内存
    		m_freePosition = reinterpret_cast<A*>(new char[realsize]);  //传统new,调用系统底层的malloc
    		ptmplink = m_freePosition;
    
    		//把分配出来的这一大块内存(5下块),彼此要链起来,供后续使用
    		for (; ptmplink != &m_freePosition[m_sTrunkCount - 1]; ptmplink++)
    		{
    			ptmplink->next = ptmplink + 1;
    		}
    		ptmplink->next = nullptr;
    		++m_iMallocCount;
    	}
    	ptmplink = m_freePosition;
    	m_freePosition = m_freePosition->next;
    	++m_iCount;
    	return ptmplink;
    #endif
    }
    
    void A::operator delete(void* phead)
    {
    #ifdef MYMEMPOOL 
    	free(phead);
    	return;
    #else
    	(static_cast<A*>(phead))->next = m_freePosition;
    	m_freePosition = static_cast<A*>(phead);
    #endif
    }
    
    void func()
    {
    	clock_t start, end;  //包含头文件#include
    	start = clock();
    	for (size_t i = 0; i < 500'0000; i++)
    	{
    		A* pa = new A();
    	}
    	end = clock();
    	cout << "申请分配内存的次数为:" << A::m_iCount << ";实际malloc的次数为:" << A::m_iMallocCount << endl;
    	cout << "用时(毫秒):" << end - start << endl;
    }
    
    
    int main(int argc, const char* argv[])
    {
    	func();
    }
    
    • 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

    三:内存池代码后续说明

    #include
    #include 
    
    using namespace std;
    
    //#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_freePosition;  //总是指向一块可以分配出去的内存首地址
    	static int m_sTrunkCount;  //一次分配多少倍的该类内存
    };
    
    int A::m_iCount = 0;
    int A::m_iMallocCount = 0;
    A* A::m_freePosition = nullptr;
    int A::m_sTrunkCount = 5;  //一次分配5倍的该类内存作为内存池的大小
    
    void* A::operator new(size_t size)
    {
    #ifdef MYMEMPOOL
    	A* ppoint = (A*)malloc(size);
    	++m_iMallocCount;
    	++m_iCount;
    	return ppoint;
    #else
    	A* ptmplink;
    	if (m_freePosition == nullptr)
    	{
    		//为空,要申请一大块内存
    		size_t realsize = m_sTrunkCount * size;  //申请m_sTrunkCount这么多倍的内存
    		m_freePosition = reinterpret_cast<A*>(new char[realsize]);  //传统new,调用系统底层的malloc
    		ptmplink = m_freePosition;
    
    		//把分配出来的这一大块内存(5下块),彼此要链起来,供后续使用
    		for (; ptmplink != &m_freePosition[m_sTrunkCount - 1]; ptmplink++)
    		{
    			ptmplink->next = ptmplink + 1;
    		}
    		ptmplink->next = nullptr;
    		++m_iMallocCount;
    		printf("%s\n", "---------------------");
    	}
    	ptmplink = m_freePosition;
    	m_freePosition = m_freePosition->next;
    	++m_iCount;
    	return ptmplink;
    #endif
    }
    
    void A::operator delete(void* phead)
    {
    #ifdef MYMEMPOOL 
    	free(phead);
    	return;
    #else
    	(static_cast<A*>(phead))->next = m_freePosition;
    	m_freePosition = static_cast<A*>(phead);
    #endif
    }
    
    void func()
    {
    	clock_t start, end;  //包含头文件#include
    	start = clock();
    	for (size_t i = 0; i < 15; i++)
    	{
    		A* pa = new A();
    		printf("%p\n", pa);
    	}
    	end = clock();
    	printf("%s\n", "---------------------");
    	cout << "申请分配内存的次数为:" << A::m_iCount << ";实际malloc的次数为:" << A::m_iMallocCount << endl;
    	cout << "用时(毫秒):" << end - start << endl;
    }
    
    
    int main(int argc, const char* argv[])
    {
    	func();
    }
    
    • 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

    此代码的delete方法并没有释放内存,也没有内存泄漏,申请的内存可重复使用。

  • 相关阅读:
    信息学奥赛一本通(c++):1407:笨小猴
    WPF自定义控件与样式(4)-CheckBox/RadioButton自定义样式
    Win10系统+Chrome浏览器+ERR_EMPTY_RESPONSE
    视觉目标检测大模型套件detrex-调研
    怎么找抖音视频素材?在哪里找爆款热门的素材呢?
    Android ViewBinding和DataBinding功能作用区别
    [思维]Party Codeforces1711B
    从零开始学习 Java:简单易懂的入门指南之Stream流(二十七)
    名词作形容词的用法
    MySQL约束和事务知识归纳。
  • 原文地址:https://blog.csdn.net/zzyzxb/article/details/128032175