zoukankan      html  css  js  c++  java
  • 内存池(MemPool)技术详解

    作者:许式伟

    来源:http://blog.csdn.net/xushiweizh/archive/2006/11/22/1402967.aspx

     

    内存池(MemPool)技术备受推崇。我用google搜索了下,没有找到比较详细的原理性的文章,故此补充一个。另外,补充了boost::pool组件与经典MemPool的差异。同时也描述了MemPoolsgi-stl/stlport中的运用。

     

    经典的内存池技术

     

     

    经典的内存池(MemPool)技术,是一种用于分配大量大小相同的小对象的技术。通过该技术可以极大加快内存分配/释放过程。下面我们详细解释其中的奥妙。

     

    经典的内存池只涉及两个常量:MemBlockSizeItemSize(小对象的大小,但不能小于指针的大小,在32位平台也就是不能小于4字节),以及两个指针变量MemBlockHeaderFreeNodeHeader。开始,这两个指针均为空。

    1. class MemPool
    2. {
    3. private:
    4.     const int m_nMemBlockSize;
    5.     const int m_nItemSize;
    6.     struct _FreeNode {
    7.         _FreeNode* pPrev;
    8.         BYTE data[m_nItemSize - sizeof(_FreeNode*)];
    9.     };
    10.     struct _MemBlock {
    11.         _MemBlock* pPrev;
    12.         _FreeNode data[m_nMemBlockSize/m_nItemSize];
    13.     };
    14.   
    15.     _MemBlock* m_pMemBlockHeader;
    16.     _FreeNode* m_pFreeNodeHeader;
    17. public:
    18.    MemPool(int nItemSize, int nMemBlockSize = 2048)
    19.        : m_nItemSize(nItemSize), m_nMemBlockSize(nMemBlockSize),
    20.          m_pMemBlockHeader(NULL), m_pFreeNodeHeader(NULL)
    21.    {
    22.    }
    23. };

            

    其中指针变量MemBlockHeader是把所有申请的内存块(MemBlock)串成一个链表,以便通过它可以释放所有申请的内存。FreeNodeHeader变量则是把所有自由内存结点(FreeNode)串成一个链。

     

    这段话涉及两个关键概念:内存块(MemBlock自由内存结点(FreeNode。内存块大小一般固定为MemBlockSize字节(除去用以建立链表的指针外)。内存块在申请之初就被划分为多个内存结点(Node),每个Node大小为ItemSize(小对象的大小),计MemBlockSize/ItemSize个。这MemBlockSize/ItemSize个内存结点刚开始全部是自由的,他们被串成链表。我们看看申请/释放内存过程,就很容易明白这样做的目的。

     

    申请内存过程

    代码如下:

    1. void* MemPool::malloc()    // 没有参数
    2. {
    3.     if (m_pFreeNodeHeader == NULL)
    4.     {
    5.        const int nCount = m_nMemBlockSize/m_nItemSize;
    6.         _MemBlock* pNewBlock = new _MemBlock;
    7.         pNewBlock->data[0].pPrev = NULL;
    8.         for (int i = 1; i < nCount; ++i)
    9.             pNewBlock->data[i].pPrev = &pNewBlock->data[i-1];
    10.         m_pFreeNodeHeader = &pNewBlock->data[nCount-1];
    11.         pNewBlock->pPrev = m_pMemBlock;
    12.         m_pMemBlock = pNewBlock;
    13.     }
    14.     void* pFreeNode = m_pFreeNodeHeader;
    15.     m_pFreeNodeHeader = m_pFreeNodeHeader->pPrev;
    16.     return pFreeNode;
    17. }

    内存申请过程分为两种情况:

    ·            在自由内存结点链表(FreeNodeList)非空。
    在此情况下,Alloc过程只是从链表中摘下一个结点的过程。
     

    ·            否则,意味着需要一个新的内存块(MemBlock)
    这个过程需要将新申请的MemBlock切割成多个Node,并把它们串起来。
    MemPool
    技术的开销主要在这。
     

    释放内存过程

     代码如下:

    1. void MemPool::free(void* p)
    2. {
    3.     _FreeNode* pNode = (_FreeNode*)p;
    4.     pNode->pPrev = m_pFreeNodeHeader;
    5.     m_pFreeNodeHeader = pNode;
    6. }

        

    释放过程极其简单,只是把要释放的结点挂到自由内存链表(FreeNodeList)的开头即可。

     

     

     

    性能分析

    MemPool技术申请内存/释放内存均极其快(比AutoFreeAlloc慢)。其内存分配过程多数情况下复杂度为O(1),主要开销在FreeNodeList为空需要生成新的MemBlock时。内存释放过程复杂度为O(1)

     

     


     

    boost::pool

    boost::pool是内存池技术的变种。主要的变化如下:

    ·            MemBlock改为非固定长度(MemBlockSize),而是:第1次申请时m_nItemSize*32,第2次申请时m_nItemSize*64,第3次申请时m_nItemSize*128,以此类推。不采用固定的MemBlockSize,而采用这种做法预测模型(是的,这是一种用户内存需求的预测模型,其实std::vector的内存增长亦采用了该模型),是一个细节上的改良。
     

    ·            增加了ordered_free(void* p) 函数。

    ordered_free
    区别于free的是,free把要释放的结点挂到自由内存链表(FreeNodeList)的开头,ordered_free则假设FreeNodeList是有序的,因此会遍历FreeNodeList把要释放的结点插入到合适的位置。

    我们已经看到,free的复杂度是O(1),非常快。但请注意ordered_free是比较费的操作,其复杂度是O(N)。这里NFreeNodeList的大小。对于一个频繁释放/申请的系统,这个N很可能是个大数。这个boost描述得很清楚:http://www.boost.org/libs/pool/doc/interfaces/pool.html

    注意:不要认为boost提供ordered_free是多此一举。后文我们会在讨论boost::object_pool时解释这一点。

     

    基于内存池技术的通用内存分配组件 


    sgi-stl
    把内存池(MemPool)技术进行发扬光大,用它来实现其最根本的allocator
     

     

    其大体的思想是,建立16MemPool<=8字节的内存申请由0MemPool分配,<=16字节的内存申请由1MemPool分配,<=24字节的内存有2MemPool分配,以此类推。最后,>128字节的内存申请由普通的malloc分配。

     

     

     

     

    注意


    以上代码属于伪代码(struct _FreeNode_MemBlock编译通不过),并且去除了出错处理。

     

  • 相关阅读:
    Zabbix设置自定义监控
    mysql数据库表的查询操作-总结
    常用软件编译参数以及软件地址
    # <center>merge表不可用的问题</center>
    不在更新了
    SVN同步大坑
    记一次ftp服务器搭建走过的坑
    详解apache的allow和deny
    libc.so.6被删后导致系统无法使用的原因及解决方法
    puppet学习笔记(二)
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6471206.html
Copyright © 2011-2022 走看看