正文7:内存池的分类、原理与优点及怎么手撕内存池(代码放github,未完待续10/4)

(13) 2024-10-05 14:01:01

文章目录

    • 1.内存池分类
      • 0)使用内存池的优点
      • 1)伙伴算法的分配,以一页大小4k举例
      • 2)slab算法
      • 3)针对一个类的内存池演示代码
        • (1)介绍
        • (2)基础代码部分
        • (3)内存池对于速度的比较
        • (4)基础代码增加嵌入式指针(目的:节省4字节的next内存)
      • 4)仿nginx代码(客户端断开,释放分配的内存)
      • 5)类模板,用链表做对象池
      • 6)类模板,用红黑树当对象池
    • 2.内存池原理及特点
    • 3.内存池代码讲解
    • 4.代码传送门

1.内存池分类

0)使用内存池的优点

①不用内存储不利于内存管理,不用内存存储池可以用单例类去分配
②不适用内存池的话,会有内存碎片
③使用了内存池利于检测内存泄漏
④节省小块内存

1)伙伴算法的分配,以一页大小4k举例

  • 分配原理

正文7:内存池的分类、原理与优点及怎么手撕内存池(代码放github,未完待续10/4) (https://mushiming.com/)  第1张
从8、16、32字节分配到2k字节

  • 使用场景

适合分配单位以页为分配单位,适合在物理内存上分配和回收,不使用在虚拟内存

2)slab算法

  • 分配原理

4K分成若干个4字节,若干个8字节,若干个16字节等等

  • 使用举例

例如stl的allocator

3)针对一个类的内存池演示代码

(1)介绍

来自王建伟《新经典C++》内存池

(2)基础代码部分
#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; } 
(3)内存池对于速度的比较
  • 设置A::m_sTrunkCount = 5
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# g++ versionWang.cpp root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# ./a.out 申请分配的内存次数为: 实际malloc的次数为: 用时(毫秒)78767 Hello world 
  • 设置A::m_sTrunkCount = 500
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# g++ versionWang.cpp root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# ./a.out 申请分配的内存次数为: 实际malloc的次数为:10000 用时(毫秒)53223 
  • 纯malloc
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# g++ versionWang.cpp root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# ./a.out 申请分配的内存次数为:0 实际malloc的次数为:0 用时(毫秒) 
  • 结论

可以看出一次分配500块内存虽然比一次分配5块内存节省分配次数,分配500W次大概少了99W次,但是时间上只相差了25000ms,用malloc的时间更多

(4)基础代码增加嵌入式指针(目的:节省4字节的next内存)
#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字节

4)仿nginx代码(客户端断开,释放分配的内存)

5)类模板,用链表做对象池

  • assertx.h
 // 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 
  • List.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 

6)类模板,用红黑树当对象池

2.内存池原理及特点

3.内存池代码讲解

4.代码传送门

THE END

发表回复