内存池的设计和实现总结(一)

回眸只為那壹抹淺笑 提交于 2020-02-13 08:19:36

  C/C++下内存管理是让几乎每一个程序员头疼的问题,分配足够的内存、追踪内存的分配、在不需要的时候释放内存——这个任务相当复杂。而直接使用系统调用malloc/free、new/delete进行内存分配和释放,有以下弊端:

  1. 调用malloc/new,系统需要根据“最先匹配”、“最优匹配”或其他算法在内存空闲块表中查找一块空闲内存,调用free/delete,系统可能需要合并空闲内存块,这些会产生额外开销
  2. 频繁使用时会产生大量内存碎片,从而降低程序运行效率
  3. 容易造成内存泄漏
  内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。
  本章先实现一个简单的内存池(CSingleMemoryPools)。该内存池提供一定数量、大小相等的内存块。该实例中,CSingleMemoryPools中的m_pMemoryFreeList负责对空闲内存块进行管理,每个内存块以_MemoryBlock类进行管理,其中首部有4个字节的指针块地址 + 4个字节的list表首地址 + 4位验证码,然后才是分配的内存。
 1 #pragma once 
 2 
 3 //开发一个简单的内存池,用于内存管理。 
 4 
 5 #include <stdio.h> 
 6 #include <stdlib.h> 
 7 #include <list>
 8 #include "ThreadLock.h" 
 9 
10 #define MAX_MEMORYHEAD_SIZE 12         //4个字节的指针块地址 + 4个字节的List表首地址 + 4位验证码 
11 #define MAGIC_CODE          0xFFFFFF   //验证码 
12 #define MEMORY_BUFFER_SIZE  1024       //该简单的内存池,提供1024字节大小的内存块
13 #define UINT32 unsigned int 
14 
15 struct _MemoryBlock  //内存块的结构,12字节head+内存空间
16 { 
17     void*         m_pBrick; 
18     _MemoryBlock()
19     { 
20         m_pBrick  = NULL;
21     }; 
22 }; 
23 
24 
25 class CSingleMemoryPools 
26 { 
27 public: 
28     static CSingleMemoryPools& Instance() 
29     { 
30         if(m_pMemoryPools == NULL) 
31         { 
32             m_pMemoryPools = new CSingleMemoryPools(); 
33         } 
34 
35         return *m_pMemoryPools; 
36     } 
37 
38 public: 
39     ~CSingleMemoryPools(void); 
40 
41     void* GetBuff();
42     bool DelBuff(void* pBuff); 
43     void DisplayMemoryList(); 
44 
45 private: 
46     CSingleMemoryPools(void); 
47     void Close(); 
48     void* SetMemoryHead(void* pBuff, std::list<_MemoryBlock*>* pList, _MemoryBlock* pBlock); 
49     void* GetMemoryHead(void* pBuff); 
50     bool GetHeadMemoryBlock(void* pBuff, std::list<_MemoryBlock*>*& pList, _MemoryBlock*& pBlock); 
51 
52 private: 
53     static CSingleMemoryPools* m_pMemoryPools; 
54     std::list<_MemoryBlock*> m_pMemoryFreeList;       //自由的内存块list
55     CThreadLock          m_ThreadLock;
56 }; 
  1 #include "SingleMemoryPools.h" 
  2 #include <iostream>
  3 
  4 CSingleMemoryPools* CSingleMemoryPools::m_pMemoryPools = NULL; 
  5 CSingleMemoryPools::CSingleMemoryPools(void) 
  6 { 
  7 
  8 } 
  9 
 10 CSingleMemoryPools::~CSingleMemoryPools(void) 
 11 { 
 12     Close(); 
 13 } 
 14 
 15 
 16 void CSingleMemoryPools::Close() 
 17 { 
 18     //添加线程安全 
 19     CAutoLock autolock(&m_ThreadLock); 
 20 
 21     //删除所有已经归还的内存块,
 22     //注:这个简单的内存池功能,关闭功能必须在所有分配出去的内存块都还回来之后才可以Close 
 23 
 24     std::list<_MemoryBlock*>::iterator itor = m_pMemoryFreeList.begin();
 25     while(itor != m_pMemoryFreeList.end())
 26     {
 27         free((*itor)->m_pBrick); 
 28         m_pMemoryFreeList.erase(itor);
 29         //兼容linux环境,linux下没有iterator erase( iterator _Where )方法, 有void erase( iterator _Where )方法
 30         itor = m_pMemoryFreeList.begin();
 31     }
 32 } 
 33 
 34 void* CSingleMemoryPools::SetMemoryHead(void* pBuff, std::list<_MemoryBlock*>* pList, _MemoryBlock* pBlock) 
 35 { 
 36     //组成内存包头 
 37     if(NULL == pBuff) 
 38     { 
 39         return NULL; 
 40     } 
 41 
 42     //因为一个long是4个字节,在linux和windows下都是一样的。所以加起来是12个 
 43     UINT32* plData = (UINT32*)pBuff; 
 44 
 45     plData[0] = (UINT32)pList;         //内存List表首地址 
 46     plData[1] = (UINT32)pBlock;        //所在链表的地址 
 47     plData[2] = (UINT32)MAGIC_CODE;    //验证码 
 48 
 49     return &plData[3]; 
 50 } 
 51 
 52 void* CSingleMemoryPools::GetMemoryHead(void* pBuff) 
 53 { 
 54     if(NULL == pBuff) 
 55     { 
 56         return NULL; 
 57     } 
 58 
 59     long* plData = (long*)pBuff; 
 60     return &plData[3]; 
 61 } 
 62 
 63 bool CSingleMemoryPools::GetHeadMemoryBlock(void* pBuff, std::list<_MemoryBlock*>*& pList, _MemoryBlock*& pBlock) 
 64 { 
 65     char* szbuf = (char*)pBuff; 
 66     UINT32* plData = (UINT32*)(szbuf - MAX_MEMORYHEAD_SIZE); 
 67     if(plData[2] != (long)MAGIC_CODE) 
 68     { 
 69         return false; 
 70     } 
 71     else 
 72     { 
 73         pList  = (std::list<_MemoryBlock*>*)plData[0];   //内存List表首地址 
 74         pBlock = (_MemoryBlock*)plData[1];  //所在链表的地址 
 75 
 76         return true; 
 77     } 
 78 
 79 } 
 80 
 81 void* CSingleMemoryPools::GetBuff() 
 82 { 
 83     //添加线程安全 
 84     CAutoLock autolock(&m_ThreadLock); 
 85 
 86     void* pBuff = NULL; 
 87 
 88     //判断是否有空闲的内存块。 
 89     if(m_pMemoryFreeList.empty()) 
 90     { 
 91         //申请内存块空间
 92         pBuff = malloc(MEMORY_BUFFER_SIZE + MAX_MEMORYHEAD_SIZE); 
 93         memcpy(pBuff,0,MEMORY_BUFFER_SIZE + MAX_MEMORYHEAD_SIZE);
 94         if(NULL == pBuff) 
 95         { 
 96             //printf_s("[CSingleMemoryPools::GetBuff] pBuff malloc = NULL.\n"); 
 97             return NULL; 
 98         } 
 99 
100         //新建一个内存块单元
101         _MemoryBlock* pMemoryUsed = new _MemoryBlock(); 
102         if(NULL == pMemoryUsed) 
103         { 
104             //printf_s("[CSingleMemoryPools::GetBuff] pMemoryBrick new = NULL.\n"); 
105             delete pBuff; 
106             return NULL; 
107         } 
108 
109         pMemoryUsed->m_pBrick = pBuff; 
110         return SetMemoryHead(pBuff, &m_pMemoryFreeList, pMemoryUsed); 
111     } 
112 
113     //已有空余内存块,由于内存块头部已经初始化过了,这边无须再初始化,直接扔出来就可以了
114     _MemoryBlock* pBlockBuff = (m_pMemoryFreeList.front());
115     m_pMemoryFreeList.pop_front();
116     return GetMemoryHead(pBlockBuff->m_pBrick);
117 } 
118 
119 bool CSingleMemoryPools::DelBuff(void* pBuff) 
120 { 
121     //添加线程安全 
122     CAutoLock autolock(&m_ThreadLock); 
123 
124     _MemoryBlock* pMemoryUsed     = NULL; 
125     std::list<_MemoryBlock*>*  pCurrMemoryList = NULL; 
126 
127     if(false == GetHeadMemoryBlock(pBuff, pCurrMemoryList, pMemoryUsed)) 
128     {
129         return false; 
130     } 
131 
132     if(NULL != pMemoryUsed && pCurrMemoryList == &m_pMemoryFreeList ) 
133     { 
134         m_pMemoryFreeList.push_back(pMemoryUsed);
135         return true; 
136     } 
137 
138     //printf_s("[CSingleMemoryPools::DelBuff] pBuff = 0x%08x is not memoryPool.\n", pBuff); 
139     return false; 
140 } 
141 
142 void CSingleMemoryPools::DisplayMemoryList() 
143 { 
144     int nFreeCount = m_pMemoryFreeList.size();
145     printf_s("[CSingleMemoryPools::DisplayMemoryList] pMemoryFree nFreeCount = %d, Size = %d.\n", nFreeCount, MEMORY_BUFFER_SIZE * nFreeCount); 
146 } 
147 
148 // TODO: 在 STDAFX.H 中 
149 // 引用任何所需的附加头文件,而不是在此文件中引用 
150 //#include "MemoryPools.h" 
151 
152 //重载New和Delete操作符 
153 inline void* operator new(size_t szBuff) 
154 { 
155     //注:由于这是一个简单的内存池,大小固定,所以参数szBuff没有用起来,后期会开发一个多层级大小的内存池
156     void* pBuff = CSingleMemoryPools::Instance().GetBuff(); 
157     //OUR_DEBUG((LM_ERROR, "[New] Size = %d Address = [0x%08x].!\n", (int)szBuff, pBuff)); 
158     return pBuff; 
159 } 
160 
161 inline void operator delete(void* p) 
162 { 
163     if(false == CSingleMemoryPools::Instance().DelBuff(p)) 
164     { 
165         //    OUR_DEBUG((LM_ERROR, "[Delete]*p = [0x%08x] false!\n", p)); 
166         //CSingleMemoryPools::Instance().DisplayMemoryList(p); 
167     } 
168     else 
169     { 
170         //OUR_DEBUG((LM_ERROR, "[Delete]*p = [0x%08x] OK!\n", p)); 
171     } 
172 } 
173 
174 inline void operator  delete[]( void * p ) 
175 { 
176     if(false == CSingleMemoryPools::Instance().DelBuff(p)) 
177     { 
178         //    OUR_DEBUG((LM_ERROR, "[Delete]*p = [0x%08x] false!\n", p)); 
179         //CSingleMemoryPools::Instance().DisplayMemoryList(p); 
180     } 
181     else 
182     { 
183         //OUR_DEBUG((LM_ERROR, "[Delete]*p = [0x%08x] OK!\n", p)); 
184     } 
185 } 

 

使用一个list来管理内存,相对于用两个list(一个FreeList ,一个 Used List)的优点:更加简洁,管理更加简单;缺陷:无法知晓已经分配出去的内存块。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!