C/C++下內存管理是讓幾乎每一個程序員頭疼的問題,分配足夠的內存、追蹤內存的分配、在不需要的時候釋放內存——這個任務相當復雜。而直接使用系統調用malloc/free、new/delete進行內存分配和釋放,有以下弊端:
- 調用malloc/new,系統需要根據“最先匹配”、“最優匹配”或其他算法在內存空閑塊表中查找一塊空閑內存,調用free/delete,系統可能需要合並空閑內存塊,這些會產生額外開銷
- 頻繁使用時會產生大量內存碎片,從而降低程序運行效率
- 容易造成內存泄漏
內存池則是在真正使用內存之前,先申請分配一定數量的、大小相等(一般情況下)的內存塊留作備用。當有新的內存需求時,就從內存池中分出一部分內存塊,若內存塊不夠再繼續申請新的內存。這樣做的一個顯著優點是,使得內存分配效率得到提升。
本章先實現一個簡單的內存池(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)的優點:更加簡潔,管理更加簡單;缺陷:無法知曉已經分配出去的內存塊。