C++ Boost Pool超详细讲解

一、提要

        什么是Boost.Pool?

        Boost Pool 是一个库,其中包含一些用于管理内存的类。虽然 C++ 程序通常使用 new 来动态分配内存,但如何提供内存的细节取决于标准库和操作系统的实现。例如,使用 Boost.Pool,您可以加速内存管理以更快地为您的程序提供内存。

        Boost.Pool 不会改变新系统或操作系统的行为。 Boost.Pool 之所以有效,是因为首先从操作系统请求托管内存——例如使用 new。从外面看,你的程序已经分配了内存,但在内部,内存还不是必需的,交给 Boost.Pool 来管理。

二、关于Boost.Pool应用

        Boost.Pool 以相同大小对内存段进行分区。每次您从 Boost.Pool 请求内存时,库都会访问下一个空闲段并将该段中的内存分配给您。然后将整个段标记为已使用,无论您实际需要该段的多少字节。

        这种内存管理概念称为简单隔离存储。这是 Boost.Pool 唯一支持的概念。如果必须经常创建和销毁许多相同大小的对象,则它特别有用。在这种情况下,可以快速提供和释放所需的内存。

        ​​ Boost.Pool 提供类 boost::simple_segregated_storage 来创建和管理隔离内存。 boost::simple_segregated_storage 是一个低级类,您通常不会在程序中直接使用它。它仅在示例1中用于说明简单的隔离存储。 Boost.Pool 中的所有其他类都在内部基于 boost::simple_segregated_storage。

案例1. 使用 boost::simple_segregated_storage

#include 
#include 
#include 
int main()
{
  boost::simple_segregated_storage storage;
  std::vector v(1024);
  storage.add_block(&v.front(), v.size(), 256);
  int *i = static_cast(storage.malloc());
  *i = 1;
  int *j = static_cast(storage.malloc_n(1, 512));
  j[10] = 2;
  storage.free(i);
  storage.free_n(j, 1, 512);
}

        必须包含头文件 boost/pool/simple_segregated_storage.hpp 才能使用类模板 boost::simple_segregated_storage。示例 4.1 将 std::size_t 作为模板参数传递。此参数指定传递给 boost::simple_segregated_storage 成员函数的数字应使用哪种类型,以引用例如段的大小。这个模板参数的实际相关性相当低。

        更有趣的是在 boost::simple_segregated_storage 上调用的成员函数。首先,调用 add_block() 将一个 1024 字节的内存块传递给存储。内存由向量 v 提供。传递给 add_block() 的第三个参数指定应将内存块划分为每个 256 字节的段。由于内存块的总大小为 1024 字节,因此存储管理的内存由四个段组成。

        对 malloc() 和 malloc_n() 的调用从存储中请求内存。 malloc() 返回指向空闲段的指针,而 malloc_n() 返回指向一个或多个连续段的指针,这些段在一个块中提供所请求的尽可能多的字节。示例 4.1 使用 malloc_n() 请求一个 512 字节的块。此调用消耗两个段,因为每个段为 256 字节。在调用 malloc() 和 malloc_n() 之后,storage 只剩下一个未使用的段。

        在示例结束时,所有段都使用 free() 和 free_n() 释放。在这两次调用之后,所有段都可用并且可以使用 malloc() 或 malloc_n() 再次请求。

        您通常不直接使用 boost::simple_segregated_storage。 Boost.Pool 提供了其他自动分配内存的类,无需您自己分配内存并将其传递给 boost::simple_segregated_storage。

案例2. 使用 boost::object_pool

#include 
int main()
{
  boost::object_pool pool;
  int *i = pool.malloc();
  *i = 1;
  int *j = pool.construct(2);
  pool.destroy(i);
  pool.destroy(j);
}

案例2  : boost::object_pool, 的定义地方为 boost/pool/object_pool.hpp.

        与 boost::simple_segregated_storage 不同,boost::object_pool 知道将存储在内存中的对象的类型。示例 4.2 中的池是用于 int 值的简单隔离存储。由池管理的内存由段组成,每个段的大小为 int——例如 4 个字节。

        一个区别是您不需要为 boost::object_pool 提供内存。 boost::object_pool 自动分配内存。在示例 4.2 中,对 malloc() 的调用使池为 32 个 int 值分配一个具有空间的内存块。 malloc() 返回一个指针,指向这 32 个段中的第一个段,一个 int 值可以完全适应。

        请注意 malloc() 返回一个 int* 类型的指针。与示例1 中的 boost::simple_segregated_storage 不同,不需要转换运算符。

        constructor() 与 malloc() 类似,但通过调用构造函数来初始化对象。在示例 4.2 中,j 指的是使用值 2 初始化的 int 对象。

        请注意,调用 struct() 时,pool 可以从 32 个段的池中返回一个空闲段。对 struct() 的调用不会使示例 4.2 从操作系统请求内存。

在示例 2 中调用的最后一个成员函数是destroy(),它会释放一个 int 对象。

示例3. 使用 boost::object_pool 更改分段大小

#include 
#include 
int main()
{
  boost::object_pool pool{32, 0};
  pool.construct();
  std::cout << pool.get_next_size() << '\n';
  pool.set_next_size(8);
}

        您可以将两个参数传递给 boost::object_pool 的构造函数。第一个参数设置当调用 malloc() 或 struct() 请求第一个段时, boost::object_pool 将分配的内存块的大小。第二个参数设置要分配的内存块的最大大小。

        如果 malloc() 或 struct() 被频繁调用,以至于内存块中的所有段都被使用,则对这些成员函数之一的下一次调用将导致 boost::object_pool 分配一个新的内存块,该内存块将是原来的两倍和上一个一样。每次 boost::object_pool 分配一个新的内存块时,大小都会加倍。 boost::object_pool 可以管理任意数量的内存块,但它们的大小会呈指数增长。第二个构造函数参数允许您限制增长。

        boost::object_pool 的默认构造函数的作用与示例 4.3 中对构造函数的调用作用相同。第一个参数将内存块的大小设置为 32 个 int 值。第二个参数指定没有最大大小。如果传递 0,则 boost::object_pool 可以无限期地使内存块的大小翻倍。

        示例3 中对 struct() 的调用使池分配 32 个 int 值的内存块。 pool 最多可以为 malloc() 或 struct() 提供 32 次调用,而无需向操作系统请求内存。如果需要更多内存,下一个要分配的内存块将有 64 个 int 值的空间。

        get_next_size() 返回下一个要分配的内存块的大小。 set_next_size() 让您设置下一个内存块的大小。在示例 4.3 中,get_next_size() 返回 64。对 set_next_size() 的调用将下一个要分配的内存块的大小从 64 更改为 8 个 int 值。使用 set_next_size() 可以直接更改下一个内存块的大小。如果您只想设置最大大小,请通过第二个参数将其传递给构造函数。

        通过 boost::singleton_pool,Boost.Pool 提供了一个介于 boost::simple_segregated_storage 和 boost::object_pool 之间的类(参见示例 4)。

案例4. 使用 boost::singleton_pool

#include 
struct int_pool {};
typedef boost::singleton_pool singleton_int_pool;
int main()
{
  int *i = static_cast(singleton_int_pool::malloc());
  *i = 1;
  int *j = static_cast(singleton_int_pool::ordered_malloc(10));
  j[9] = 2;
  singleton_int_pool::release_memory();
  singleton_int_pool::purge_memory();
}

        boost::singleton_pool 在 boost/pool/singleton_pool.hpp 中定义。此类与 boost::simple_segregated_storage 类似,因为它还期望段大小作为模板参数,而不是要存储的对象的类型。这就是为什么诸如 ordered_malloc() 和 malloc() 之类的成员函数返回一个 void* 类型的指针,它必须显式地强制转换。

        这个类也类似于 boost::object_pool,因为它会自动分配内存。下一个内存块的大小和可选的最大大小作为模板参数传递。这里的 boost::singleton_pool 与 boost::object_pool 不同:您不能在运行时更改 boost::singleton_pool 中下一个内存块的大小。

        如果您想管理多个内存池,可以使用 boost::singleton_pool 创建多个对象。传递给 boost::singleton_pool 的第一个模板参数是一个标签。标记是用作内存池名称的任意类型。示例 4.4 使用结构 int_pool 作为标签来强调 singleton_int_pool 是一个管理 int 值的池。多亏了标签,多个单例可以管理不同的内存池,即使大小的第二个模板参数相同。该标签除了创建单独的 boost::singleton_pool 实例之外没有其他用途。

        boost::singleton_pool 提供了两个成员函数来释放内存:release_memory() 释放所有当前未使用的内存块,purge_memory() 释放所有内存块——包括当前正在使用的内存块。对 purge_memory() 的调用会重置 boost::singleton_pool。

        release_memory() 和 purge_memory() 将内存返回给操作系统。要将内存返回到 boost::singleton_pool 而不是操作系统,请调用成员函数,例如 free() 或 ordered_free()。

        boost::object_pool 和 boost::singleton_pool 允许你明确地请求内存。为此,您可以调用 malloc() 或 struct() 等成员函数。 Boost.Pool 还提供了 boost::pool_allocator 类,您可以将其作为分配器传递给容器(参见示例 4.5)。

案例5. 使用 boost::pool_allocator

#include 
#include 
int main()
{
  std::vector> v;
  for (int i = 0; i < 1000; ++i)
    v.push_back(i);
  v.clear();
  boost::singleton_pool::
    purge_memory();
}

        boost::pool_allocator 在 boost/pool/pool_alloc.hpp 中定义。该类是一个分配器,通常作为第二个模板参数从标准库传递给容器。分配器提供容器所需的内存。

        boost::pool_allocator 基于 boost::singleton_pool。要释放内存,您必须使用标签访问 boost::singleton_pool 并调用 purge_memory() 或 release_memory()。示例 4.5 使用标签 boost::pool_allocator_tag。此标签由 Boost.Pool 定义,并由 boost::pool_allocator 用于内部 boost::singleton_pool。

        当示例 4.5 第一次调用 push_back() 时,v 访问分配器以获取请求的内存。因为使用了分配器 boost::pool_allocator,所以分配了一个具有 32 个 int 值空间的内存块。 v 接收指向该内存块中具有 int 大小的第一个段的指针。每次后续调用 push_back() 时,都会使用内存块中的另一个段,直到分配器检测到需要更大的内存块。

        请注意,在使用 purge_memory() 释放内存之前,应在容器上调用 clear()(参见示例 4.5)。调用 purge_memory() 会释放内存,但不会通知容器它不再拥有内存。调用 release_memory() 的危险性较小,因为它只释放未使用的内存块。

        Boost.Pool 还提供了一个名为 boost::fast_pool_allocator 的分配器(参见示例 4.6)。

示例6. Using boost::fast_pool_allocator

#define BOOST_POOL_NO_MT
#include 
#include 
int main()
{
  typedef boost::fast_pool_allocator allocator;
  std::list l;
  for (int i = 0; i < 1000; ++i)
    l.push_back(i);
  l.clear();
  boost::singleton_pool::
    purge_memory();
}

        两种分配器的使用方式相同,但如果您请求连续的段,则应该首选 boost::pool_allocator。 boost::fast_pool_allocator 可以用于分段请求的情况。大体简化:您将 boost::pool_allocator 用于 std::vector 和 boost::fast_pool_allocator 用于 std::list。

        示例  6 说明了哪些模板参数可以传递给 boost::fast_pool_allocator。 boost::pool_allocator 接受相同的参数。

        boost::default_user_allocator_new_delete 是一个使用 new 分配内存块并使用 delete[] 释放它们的类。您还可以使用 boost::default_user_allocator_malloc_free,它调用 malloc() 和 free()。

        boost::details::pool::default_mutex 是设置为 boost::mutex 或 boost::details::pool::null_mutex 的类型定义。 boost::mutex 是支持多个线程从分配器请求内存的默认类型。如果宏 BOOST_POOL_NO_MT 的定义如示例 4.6 中所示,则禁用 Boost.Pool 的多线程支持。示例 4.6 中的分配器使用空互斥体。

        在示例  6 中传递给 boost::fast_pool_allocator 的最后两个参数设置了第一个内存块的大小和要分配的内存块的最大大小。

Chapter 1. Boost.SmartPointers (theboostcpplibraries.com)

到此这篇关于C++ Boost Pool超详细讲解的文章就介绍到这了,更多相关C++ Boost Pool内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的:(C++ Boost Pool超详细讲解)