boost 内存池

来源:互联网 发布:enroute3软件下载 编辑:程序博客网 时间:2024/05/29 08:58
1. 什么是内存池
    “池”是在计算机技术中经常使用的一种设计模式,其内涵在于:将程序中需要经常使用的核心资源先申请出来,放到一个池内,由程序自己管理,这样可以提高资源的使用效率,也可以保证本程序占有的资源数量。经常使用的池技术包括内存池、线程池和连接池等,其中尤以内存池和线程池使用最多。
    内存池(MemoryPool)是一种动态内存分配与管理技术。通常情况下,程序员习惯直接使用new、delete、malloc、free等API申请分配和释放内存,导致的后果是:当程序长时间运行时,由于所申请内存块的大小不定,频繁使用时会造成大量的内存碎片从而降低程序和操作系统的性能。内存池则是在真正使用内存之前,先申请
分配一大块内存(内存池)留作备用,当程序员申请内存时,从池中取出一块动态分配,当程序员释放内存时,将释放的内存再放入池内,并尽量与周边的空闲内存块合并。若内存池不够时,则自动扩大内存池,从操作系统中申请更大的内存池。
内存池的应用场景
    早期的内存池技术是为了专门解决那种频繁申请和释放相同大小内存块的程序,因此早期的一些内存池都是用相同大小的内存块链表组织起来的。
    Boost的内存池则对内存块的大小是否相同没有限制,因此只要是频繁动态申请释放内存的长时间运行程序,都适用Boost内存池。这样可以有效减少内存碎片并提高程序运行效率。内存池内存重分配机制:再次申请的内存为2*当前内存池的大小。
2. 内存池的特征
  • 2.1 无内存泄露
    • 正确的使用内存池的申请和释放函数不会造成内存泄露,更重要的是,即使不正确的使用了申请和释放函数,内存池中的内存也会在进程结束时被全部自动释放,不会造成系统的内存泄露。
  • 2.2 申请的内存数组没有被填充
    • 例如一个元素的内存大小为A,那么元素数组若包含n个元素,则该数组的内存大小必然是A*n,不会有多余的内存来填充该数组。尽管每个元素也许包含一些填充的东西。
  • 2.3 任何数组内存块的位置都和使用operator new[]分配的内存块位置一致
    • 这表明你仍可以使用那些通过数组指针计算内存块位置的算法。
  • 2.4 内存池要比直接使用系统的动态内存分配快
    • 这个快是概率意义上的,不是每个时刻,每种内存池都比直接使用new或者malloc快。例如,当程序使用内存池时内存池恰好处于已经满了的状态,那么这次内存申请会导致内存池自我扩充,肯定比直接new一块内存要慢。但在大部分时候,内存池要比new或者malloc快很多。

3. Boost内存池的分类
Boost内存池按照不同的理念分为四类。主要是两种理念的不同造成了这样的分类。

  • 一是 Object Usage 和 Singleton Usage 的不同。
    • Object Usage 意味着每个内存池都是一个可以创建和销毁的对象,一旦内存池被销毁则其所分配的所有内存都会被释放。
    • Singleton Usage 意味着每个内存池都是一个被静态分配的对象,直至程序结束才会被销毁,这也意味着这样的内存池是多线程安全的。只有使用 release_memory 或者 purge_memory 方法才能释放内存。
  • 二是内存溢出的处理方式。
    • 第一种方式是返回 NULL 代表内存池溢出了;
    • 第二种方式是抛出异常代表内存池溢出。
根据以上的理念,boost的内存池分为四种。
  1. pool 是一个 Object Usage 的内存池,溢出时返回 NULL。
  2. object_pool 与 pool 类似,唯一的区别是当其分配的内存释放时,它会尝试调用该对象的析购函数。
  3. singleton_pool 是一个 Singleton Usage 的内存池,溢出时返回 NULL。
  4. pool_alloc 是一个 Singleton Usage 的内存池,溢出时抛出异常。

pool

  • 头文件为 <boost/pool/pool.hpp>
  • pool 主要用于快速分配小块内存,使用时需要指定每次要分配的内存块的大小。
  • malloc 函数用于从内存池中分配内存,不会调用构造函数,返回 void*;

  • free 函数用于释放内存,并交还给内存池,而不是系统;
  • release_memory 函数用于释放所有未被分配的内存;
  • purge_memory 函数用于释放所有内存。
  • 当然,也可以不调用 free 或 release_memory 等函数,pool 接口对象在析构时会调用 purge_memory 自动释放所有内存。
使用举例:
    使用时,指定分配块的大小。
pool<>  myPool(sizeof(int));for (int i = 0; i < 10; i++){    int *pnNum = (int *)myPool.malloc();    *pnNum = i+1;    cout << *pnNum << endl;}
object_pool 
  • 头文件为 <boost/pool/object_pool.hpp>
  • object_pool 主要用于对象的内存分配并自动调用类的构造函数。
  • 其 construct 函数用于从内存池中分配内存并自动调用构造函数,返回指向类型的指针

  • 其 destroy 函数用于释放内存交还给内存池并自动调用析构函数。
  • 与 pool 接口一样,也可以不调用 destroy 函数,object_pool 接口对象在析构时会自动释放所有内存并自动调用析构函数。
  • 另外,object_pool 接口也有 malloc 和 free 函数,
    • malloc 只分配内存而不负责构造,返回指向类型的指针
    • free 只释放内存而不负责析构。
  • 因此,最好将 construct 和 destroy 配对使用,将 malloc 和 free 配对使用,而不要两者混用。
使用举例:
    指定分配对象的类型
object_pool<CTest>  myObjectPool;for (int j = 0; j < 10; j++){    CTest *pTest = (CTest *)myObjectPool.construct(j*j);    if (j == 5)    {        myObjectPool.destroy(pTest);    }}
singleton_pool 
  • 头文件为 <boost/pool/singleton_pool.hpp>
  • singleton_pool 接口的构造函数是私有的,因此不能够创建一个 singleton_pool 接口的对象。
  • singleton_pool 接口提供了一些静态方法如 malloc、free 用于内存的分配和释放,其他方面与 pool 接口相同
使用举例:
    可以定义多个pool类型的object,都是分配同样大小的内存块;singleton_pool提供静态成员方法分配内存,不用定义object。
struct intpool { };struct intpool2 { };typedef singleton_pool<intpool, sizeof(int)> ipool1;typedef singleton_pool<intpool2, sizeof(int)> ipool2;for (int i = 0; i < 10; ++i){    int *q1 = (int *)ipool1::malloc();    int *q2 = (int *)ipool2::malloc();    *q1 = i;    *q2 = i*i;    cout << *q1 << " and " << *q2 << endl;}ipool1::purge_memory();ipool2::purge_memory();
pool_allocator 
  • 头文件为 <boost/pool/pool_alloc.hpp>,主要与STL的容器一起使用,可用于代替 STL 中的 allocator
  • pool_allocator的内部实现调用了 ordered_malloc 和 ordered_free,可以满足对大量的连续内存块的分配请求。
使用举例:
void func(){    std::vector<int, boost::pool_allocator<int> > v;    for (int i = 0; i < 10000; ++i)        v.push_back(13);}
文章来源:
  1. Boost.pool内存池
  2. Boost库之pool的使用

0 0