①不用内存储不利于内存管理,不用内存存储池可以用单例类去分配
②不适用内存池的话,会有内存碎片
③使用了内存池利于检测内存泄漏
④节省小块
内存
从8、16、32字节分配到2k字节
适合分配单位以页为分配单位,适合在
物理内存
上分配和回收,不使用在虚拟内存
上
4K分成若干个4字节,若干个8字节,若干个16字节等等
例如stl的allocator
来自王建伟《新经典C++》内存池
#include <iostream> #include <ctime> typedef long clock_t; 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次数,每malloc一次加1 private: A* next; static A* m_FreePosi; //总是指向一块可以分配出去的内存的首地址 static int m_sTrunkCount; //一次分配多少倍该类的内存 }; void* A::operator new(size_t size) {
//不再用传统方式实现,而是用内存池实现 //传统方式 //A* ppoint = (A*)malloc(size); //return ppoint; //1.初始化内存池 A* tmplink; if(m_FreePosi == nullptr) {
//2.为空,我们要申请一大块内存 size_t realsize = m_sTrunkCount*size; //要申请m_sTrunkCount这么多的内存 m_FreePosi = reinterpret_cast<A*>(new char[realsize]);//传统new,调用底层传统malloc tmplink = m_FreePosi; //3.把分配的内存链接起来 for(;tmplink !=&m_FreePosi[m_sTrunkCount - 1]; ++tmplink) {
tmplink->next = tmplink + 1; } tmplink->next = nullptr; ++m_iMallocCount; } //4.归位 tmplink = m_FreePosi; m_FreePosi = m_FreePosi->next;//m_FreePosi总是指向能分配内存的下一块首地址 ++m_iCount; return tmplink; } void A::operator delete(void* phead) {
//1.传统 //free(phead); //2.内存池做法 (static_cast<A*>(phead)->next) = m_FreePosi; m_FreePosi = static_cast<A*>(phead); } int A::m_iCount = 0; int A::m_iMallocCount = 0; A* A::m_FreePosi = nullptr; int A::m_sTrunkCount = 500; //主要用来测试时间 int main() {
//和时间有关的类型 clock_t start,end; start = clock(); for(int i = 0; i< ;++i) {
A* pa = new A(); } end = clock(); std::cout<<"申请分配的内存次数为:"<<A::m_iCount<<std::endl; std::cout<<"实际malloc的次数为:"<<A::m_iMallocCount<<std::endl; std::cout<<"用时(毫秒):"<<end-start<<std::endl; return 0; }
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# g++ versionWang.cpp root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# ./a.out 申请分配的内存次数为: 实际malloc的次数为: 用时(毫秒):78767 Hello world
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# g++ versionWang.cpp root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# ./a.out 申请分配的内存次数为: 实际malloc的次数为:10000 用时(毫秒):53223
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# g++ versionWang.cpp root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# ./a.out 申请分配的内存次数为:0 实际malloc的次数为:0 用时(毫秒):
可以看出一次分配500块内存虽然比一次分配5块内存节省分配次数,分配500W次大概少了99W次,但是时间上只相差了25000ms,用malloc的时间更多
#include <iostream> #include <ctime> typedef long clock_t; class myallocator {
public: //分配内存接口 void* allocate(size_t size) {
obj* tmplink; if(m_FreePosi == nullptr) {
//为空,我要申请空间,要申请一大块内存 size_t realsize = m_sTrunkCout * size;//申请m_sTrunkCout这么多倍的内存 m_FreePosi = (obj*)malloc(realsize); tmplink = m_FreePosi; //把分配出来的这一块内存(5小块)彼此链接起来,供后续使用 for(int i = 0 ;i < m_sTrunkCout - 1; ++i) {
tmplink->next = (obj*)((char*)tmplink + size); tmplink = tmplink->next; }//end for tmplink->next = nullptr; }//end if tmplink = m_FreePosi; m_FreePosi = m_FreePosi->next; return tmplink; } void dellocate(void* phead) {
((obj*)phead)->next = m_FreePosi; m_FreePosi = (obj*)phead; } private: //写在类里面的结构 struct obj {
struct obj* next; //这个next就是一个嵌入式指针 }; int m_sTrunkCout = 5; //一次分配5倍的该类内存作为内存池的大小 obj* m_FreePosi = nullptr; }; class A {
public: //必须保证sizeof(A)凑够4字节,这里两个int成员8字节了,所以使用类myallocator毫无问题 int m_i; int m_j; public: static myallocator myalloc; //静态成员变量,跟着类A走 static void* operator new(size_t size) {
return myalloc.allocate(size); } static void operator delete(void* phead) {
return myalloc.dellocate(phead); } }; myallocator A::myalloc; //在类A之外定义一下这个静态成员变量 int main() {
A* mypa[100]; for(int i = 0;i< 15;++i) {
mypa[i] = new A(); printf("%p\n",mypa[i]); } for(int i = 0;i<15;++i) {
delete mypa[i]; } return 0; }
每个对象间隔8字节
// assertx.h - simple assertion functionality. // // By Paul Glinker // #ifndef ASSERTX_H #define ASSERTX_H #ifdef NDEBUG #define ASSERT( exp ) #else // !def NDEBUG #ifdef _WINDOWS #include <windows.h> #include <stdio.h> #include <stdlib.h> inline void assert_windows(const char *szExp, const char *szFilename, int iLineNum) {
char szMsg[128]; sprintf(szMsg, "Expr: (%s)\nFile: %s\nLine: %d\n", szExp, szFilename, iLineNum); MessageBox(NULL,szMsg,"ASSERTION FAILED!", (MB_OK|MB_ICONERROR)); exit(1); } #define ASSERT( exp ) if ( !(exp) ) assert_windows( #exp, __FILE__, __LINE__ ) #else // !def _WINDOWS #include <stdio.h> #include <stdlib.h> inline void assert_generic(const char *szExp, const char *szFilename, int iLineNum) {
printf("================================================\n"); printf("ASSERTION FAILED!\nExpr: (%s)\nFile: %s\nLine: %d\n", szExp, szFilename, iLineNum); printf("================================================\n"); exit(1); } #define ASSERT( exp ) if ( !(exp) ) assert_generic( #exp, __FILE__, __LINE__ ) #endif //_WINDOWS #endif // NDEBUG #endif // ASSERTX_H
// TFreeList.h - a templated freelist class. // // By Paul Glinker // #ifndef TFreeList_h #define TFreeList_h #include "assertx.h" template <class FLDataType> class TFreeList {
public: // Construct a TFreeList with the specified number // of objects available for allocation. TFreeList(int iNumObjects) {
ASSERT( (iNumObjects > 0) && "Invalid TFreeList size specified." ); m_pObjectData = new FLDataType[iNumObjects]; m_ppFreeObjects = new FLDataType*[iNumObjects]; ASSERT( m_pObjectData && "Not enough memory to allocate object data!" ); ASSERT( m_ppFreeObjects && "Not enough memory to allocate pointer stack!" ); m_iNumObjects = iNumObjects; m_bFreeOnDestroy = true; FreeAll(); } // Constructs a TFreeList with the specified number // of objects available for allocation from pre-allocated // memory referenced by "pObjectData" and "ppFreeObjects". // Note that pObjectData and ppFreeObjects must have // "iNumObjects" elements each. TFreeList(FLDataType *pObjectData, FLDataType **ppFreeObjects, int iNumObjects) {
ASSERT( (iNumObjects > 0) && "Invalid TFreeList size specified." ); m_pObjectData = pObjectData; m_ppFreeObjects = ppFreeObjects; ASSERT( m_pObjectData && "A NULL pointer was passed for the object data!" ); ASSERT( m_ppFreeObjects && "A NULL pointer was passed for the pointer stack!" ); m_iNumObjects = iNumObjects; m_bFreeOnDestroy = false; FreeAll(); } ~TFreeList(void) {
// If we have allocated memory, // then we must free it. if (m_bFreeOnDestroy) {
delete [] m_ppFreeObjects; delete [] m_pObjectData; } } // Returns a pointer to a free instance of FLDataType. FLDataType *NewInstance(void) {
ASSERT( m_iTop && "Tried to get a new instance but there" "were no free instances available for " "allocation. Consider using GetFree()!" ); return m_ppFreeObjects[--m_iTop]; } // Reclaims the instance referenced by pInstance. void FreeInstance(FLDataType *pInstance) {
ASSERT( (pInstance >= &(m_pObjectData[0])) && (pInstance <= &(m_pObjectData[m_iNumObjects-1])) && "Tried to free an object that was" "not from this TFreeList" ); // You might consider putting a debug-only check here to make sure // the instance that is being freed isn't already free. ASSERT( (m_iTop < m_iNumObjects) && "You have freed at least one" "instance more than once." ); m_ppFreeObjects[m_iTop++] = pInstance; } // Makes all instances available for allocation. void FreeAll(void) {
int iIndex = (m_iNumObjects-1); for (m_iTop = 0; m_iTop < m_iNumObjects; m_iTop++) {
m_ppFreeObjects[m_iTop] = &(m_pObjectData[iIndex--]); } } // Returns the total number of objects // managed by this TFreeList. int GetSize(void) {
return m_iNumObjects; } // Returns the number of instances available // for allocation. int GetFree(void) {
return m_iTop; } private: // Points to the array of objects. FLDataType *m_pObjectData; // The number of objects in m_pObjectData. int m_iNumObjects; // Points to an array of pointers to free // objects within m_pObjectData. Used as // a fixed sized stack. FLDataType **m_ppFreeObjects; // Keeps track of the first available object in // m_ppFreeObjects (the top of the stack). int m_iTop; // Remembers weather or not to free memory on // destruction. bool m_bFreeOnDestroy; }; #endif // TFreeList_h