`

内存池(MemPool)技术详解 - 经典内存池

    博客分类:
  • C++
 
阅读更多
转自
http://cplusplus.wikidot.com/cn:dive-into-memory-pool

概述

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


经典的内存池技术

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

    经典的内存池只涉及两个常量:MemBlockSize、ItemSize(小对象的大小,但不能小于指针的大小,在32位平台也就是不能小于4字节),以及两个指针变量MemBlockHeader、FreeNodeHeader。开始,这两个指针均为空。
class MemPool
{
private:
    const int m_nMemBlockSize;
    const int m_nItemSize;
 
    struct _FreeNode {
        _FreeNode* pPrev;
        BYTE data[m_nItemSize - sizeof(_FreeNode*)];
    };
 
    struct _MemBlock {
        _MemBlock* pPrev;
        _FreeNode data[m_nMemBlockSize/m_nItemSize];
    };
 
    _MemBlock* m_pMemBlockHeader;
    _FreeNode* m_pFreeNodeHeader;
 
public:
   MemPool(int nItemSize, int nMemBlockSize = 2048)
       : m_nItemSize(nItemSize), m_nMemBlockSize(nMemBlockSize),
         m_pMemBlockHeader(NULL), m_pFreeNodeHeader(NULL)
   {
   }
};


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

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

申请内存过程

代码如下:

void* MemPool::malloc()    // 没有参数
{
    if (m_pFreeNodeHeader == NULL)
    {
       const int nCount = m_nMemBlockSize/m_nItemSize;
        _MemBlock* pNewBlock = new _MemBlock;
        pNewBlock->data[0].pPrev = NULL;
        for (int i = 1; i < nCount; ++i)
            pNewBlock->data[i].pPrev = &pNewBlock->data[i-1];
        m_pFreeNodeHeader = &pNewBlock->data[nCount-1];
        pNewBlock->pPrev = m_pMemBlock;
        m_pMemBlock = pNewBlock;
    }
    void* pFreeNode = m_pFreeNodeHeader;
    m_pFreeNodeHeader = m_pFreeNodeHeader->pPrev;
    return pFreeNode;
}


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

    在自由内存结点链表(FreeNodeList)非空。在此情况下,Alloc过程只是从链表中摘下一个结点的过程。
    否则,意味着需要一个新的内存块(MemBlock)。这个过程需要将新申请的MemBlock切割成多个Node,并把它们串起来,MemPool技术的开销主要在这。


释放内存过程


代码如下:
void MemPool::free(void* p)
{
    _FreeNode* pNode = (_FreeNode*)p;
    pNode->pPrev = m_pFreeNodeHeader;
    m_pFreeNodeHeader = pNode;
}


释放过程极其简单,只是把要释放的结点挂到自由内存链表(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,而采用这种做法预测模型2,是一个细节上的改良。
增加了ordered_free(void* p) 函数。ordered_free区别于free的是,free把要释放的结点挂到自由内存链表(FreeNodeList)的开头, ordered_free则假设FreeNodeList是有序的,因此会遍历FreeNodeList把要释放的结点插入到合适的位置。我们已经看到,free的复杂度是O(1),非常快。但请注意ordered_free是比较费的操作,其复杂度是O(N)。这里N是FreeNodeList的大小。对于一个频繁释放/申请的系统,这个N很可能是个大数。这个boost描述得很清楚:http://www.boost.org/libs/pool/doc/interfaces/pool.html
注意:不要认为boost提供ordered_free是多此一举。后文我们会在讨论boost::object_pool时解释这一点。


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

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

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


内存池技术的缺陷

    遗憾的是,MemPool技术可能导致内存占用只增不减。还没有非常有效的办法去避免这种情况的发生。不过我后来在ScopeAlloc的实现中发现BlockPool(是一个简化版的内存池)反倒避免了这一缺陷。

注意

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

Footnotes
    1. boost:pool/object_pool 中称之为块(Chunk)。
    2. 是的,这是一种用户内存需求的预测模型,其实std::vector的内存增长亦采用了该模型。
分享到:
评论

相关推荐

    经典的内存池技术经典的内存池技术

    ### 经典内存池技术详解 #### 一、引言 经典的内存池技术是一种高效的内存管理方式,尤其适用于需要频繁创建和销毁大量相同大小小对象的场景。它通过预分配和重用内存来减少内存分配与释放所带来的开销,从而显著...

    mempool-开源

    `mempool` 提供了一种内存池的概念,它在程序启动时预先分配一大块内存,然后根据需求从这个内存池中分配小块内存,避免了频繁调用系统级别的内存分配函数,从而提高了性能。 内存池的基本原理是将大块内存分割成多...

    eCos内存管理.pdf

    ### eCos内存管理详解 #### 引言 eCos(embedded Configurable Operating System)是一款用于嵌入式系统的可配置实时操作系统。它提供了丰富的内核功能和广泛的设备驱动支持,允许开发者根据具体应用需求定制系统...

    dpdk学习资料-经典

    #### 六、内存池库(Mempool Library) **6.1 Cookies** 这部分内容介绍了内存池库中的Cookies机制,类似于malloc库中的Cookies,用于记录附加信息。 **6.2 统计信息** 为了便于监控和调试,DPDK的内存池库提供了...

    详解DPDK和SPDK技术知识点

    除了EAL,还有数据包处理库(Librte_Packet)和一致性内存管理库(Librte_Mempool)等,它们提供了高效的数据包处理和内存管理功能。 **5.3 DPDK 环境抽象层** EAL通过屏蔽底层硬件差异,使得DPDK库可以在多种平台...

    Lwip之内存以及包缓冲管理.pdf

    ### LwIP之内存及包缓冲管理详解 #### 一、概述 LwIP(Lightweight IP)是一款轻量级的TCP/IP协议栈,适用于资源有限的嵌入式系统。LwIP通过优化的设计和高效的内存管理机制,在有限的硬件资源上实现了完整的TCP/IP...

    DPDK编程指南

    - **内存池管理(librte_mempool)**:负责高效地分配和回收固定大小的对象,减少了频繁调用系统内存管理函数所带来的开销。 - **网络报文缓冲区管理(librte_mbuf)**:管理着网络数据包的内存布局,每个报文都对应...

    uCOS-II源码

    - **os_mempool.c**:内存池管理。 - **os_event.c**:信号量、互斥量和消息队列的实现。 - **os_flag.c**:标志组管理。 - **os_time.c**:时间管理,包括延时和定时器。 四、源码学习路径 1. **理解数据结构**:...

    eCos Native C API

    2. **cyg_mempool_destroy**:销毁内存池。当内存池不再需要时,可以通过此函数释放其占用的资源。 3. **cyg_mempool_alloc**:分配内存块。从内存池中分配一个内存块。 4. **cyg_mempool_free**:释放内存块。...

    uC/OS-II 2.90内核源代码

    4. os_mempool.c:内存池管理的实现,包括内存分配和释放。 5. os_sem.c:信号量的创建、删除和操作函数。 6. os_msgq.c:消息队列的实现,用于任务间的数据传递。 7. os_timer.c:定时器功能的实现,包括周期性...

    dpdk相关资料合集-1

    4. **Mempool**:内存池机制用于高效管理内存分配和释放,减少内存碎片。 5. **Core Bindings**:DPDK允许用户将CPU核心绑定到特定的网络接口,确保数据包处理在特定核上连续执行,避免多核间的通信开销。 6. **...

    uC/OS - II源码

    - **os_mempool.c**:内存池管理,分配和释放内存。 - **os_sem.c**、**os_msg.c**、**os_q.c**:信号量、消息队列和邮箱的实现。 通过深入学习和理解这些源码,开发者可以更好地利用uC/OS-II为嵌入式系统提供稳定...

    uc-os-II系统的移植

    同时,理解uC/OS-II的内存分配策略,如OS_Mem和OS_MemPool,以便有效利用内存。 2. **中断与任务切换**:中断服务不能直接访问任务级的数据,需通过信号量、邮箱等方式与任务通信。中断服务完成后,通过`...

    Keil_C51库函数原型列表

    - `void init_mempool(void *data *p, unsigned int size)`:初始化内存池。 - `void *malloc(unsigned int size)`:分配指定大小的内存。 - `int rand(void)`:生成随机数。 - `void *realloc(void xdata *p, ...

    ucos2操作系统源代码

    同时,"os_mempool.c"管理内存池,允许预先分配内存以提高效率。 四、信号量与互斥锁 UCOS2通过信号量和互斥锁实现资源的同步和保护。"os_sem.c"中的"OSSemCreate()"创建信号量,"OSSemPend()"和"OSSemPost()"分别...

    linux驱动工程师面试题整理.doc

    23. **内存分配函数**:`kmalloc`用于小块内存分配,`__get_free_page`分配整页内存,`mempool_create`创建内存池。 24. **IRQ与FIQ**:IRQ是常规中断,FIQ是快速中断,MIPS等处理器中,FIQ通常用于高速数据传输或...

    Facebook的c++开源库folly

    9. **Mempool**: 内存池技术,用于提高内存分配和释放的效率。 10. **Thrift**: 跨语言的服务定义和通信框架,Folly与Thrift有紧密的集成。 ### 使用Folly的益处 1. **提高代码质量**: Folly遵循最佳实践,使用...

    uCOSV251源码

    5. os_mempool.c/h:内存池管理,用于动态分配和回收内存。 6. os_flag.c/h:事件标志组的操作函数。 7. os_mutext.c/h:互斥量的相关操作函数。 三、uCOSV251的安装与使用 1. 安装:提供的uCOSV251.exe文件是安装...

    s7-300功能基础入门模块

    ### SIMOTION V4.0 Web 诊断功能详解 #### 一、SIMOTION V4.0 Web 诊断功能概述 SIMOTION V4.0是西门子公司推出的一款高性能运动控制系统,其集成了Web诊断功能,允许用户通过标准浏览器访问SIMOTION CPU的信息。...

Global site tag (gtag.js) - Google Analytics