GNU C++ Allocator分类总结与归纳

文章目录

    • 1. new_allocator & malloc_allocator
    • 2. 智能 Allocator
      • 2.1 智能 Allocator 概念、思路和分类实现
      • 2.2 bitmap_allocator


本文以GNU C++为例子进行总结归纳,主要是对GNU C++当中的 Allocator 的类别和个中思想进行分别讲解和整理。

同时经过之前的系列文章,可以知道 Allocator 主要用于满足容器中的 Element 进行空间的分配任务需求。也即是,当客户将元素加入容器中,容器必须分配更多内存以保存这些元素,于是容器向其所含的 Allocator(?有待商榷,有些时候Allocator是单独实例对象,也有可能是共享一个实例对象) 发出请求,该模板参数往往另命名为(aliased to) allocator_type

实现上,将 chars 加入到 string class 也和上段内容的步骤相同,因为 string 也算作 STL 的一种正规容器。

每个元素类型为 T 的容器的 Allocator 模板实参默认为 allocator。其接口只有大约20个 public 声明,包括嵌套的(nested) typedefs 和 成员函数。最重要的两个成员函数是:

  • T* allocate(size_type n, const void* hint = 0);
  • void deallocate(T* p, size_type n);

其中,n 指的是客户申请的元素个数,而非指的是空间总量。这些空间通过调用 ::operator new 获得,但是何时调用以及多么频繁调用,并无具体指定。

在较新版本(GNU 2.9之后?并不准确)中,Class Allocator 只有 typedef,constructor 和 rebind 等成员。同时继承自一个 xx_alloc_base 基类(high-speed extension allocators)。因此,所有的分配和归还(allocation and deallocations)操作都取决于该 base class,而这个 base class 也许是客户无法访问和操控的(user-configurable)。

为了实现以上两个成员函数,就需设计相关的数据结构和内存分配机制,为此 GNU C++ 当中定义了 7 中类型的相关类型的 Allocator 的 class,以下进行分别介绍。



1. new_allocator & malloc_allocator

每当容器需要内存就调用 operator new,每当容器释放内存就 operator delete。 这种做法比起分配大块内存并缓存(caching)再一个个block分配的做法时间上会慢一些,但是可在更大范围的操作系统和硬件上有效运作。以下①②即是使用该思想的所作的 Allocator class:

  • __gnu_cxx::new_allocator(调用::operator new()和::operator delete()实现allocator功能);
  • __gnu_cxx::malloc_allocator(实现上与new_allocator的不同仅在于使用c函数std::malloc()和std::free());

具体实现如下图所示:

GNU C++ Allocator分类总结与归纳_第1张图片
__gnu_cxx::new_allocator 实现
GNU C++ Allocator分类总结与归纳_第2张图片
__gnu_cxx::malloc_allocator 实现


2. 智能 Allocator

以下内容即是 分配大块内存并缓存(caching)再一个个block分配 的做法。


2.1 智能 Allocator 概念、思路和分类实现

所谓的智能型 Allocator,就是在 1 中所提到的,将分配所得的内存加以缓存(cache)。这种额外加入的机制以以下两种思路实现:

bitmap index 思路。即用一个索引以 2 的指数倍数成长的桶子(exponentially increasing power-of-two-sized buckets)。使用该思想实现的 Allocator class 有:

  • __gnu_cxx::bitmap_allocator;

是一个高效能的 allocator,使用 bit-map 追踪被使用和未被使用(used and unused)的内存块。

固定大小内存池缓存(fixed-size pooling cahce)思路。这里所指的 cache 被程序内的所有容器共享,而 operator new 和 operator delete 并不经常被调用,其最大优势是可省去大量的内存控制信息建置所造成的非数据内存空间的占用,在建置时间方面所节省的时间在现下的技术和硬件条件下并不是很明显。使用该思想实现的 Allocator class 有:

  • __gnu_cxx::pool_allocator:
    在 std::allocator——以GNU2.9为例 中可知,GNU2.9 容器使用的分配器不是 std::allocator 而是 std::alloc,而迭代至 GNU4.9 版本之后,GNU2.9 当中的 std::allocator 化身为 __pool_alloc。具体的内容可见上面的博文传送门。大体上就是 16 个节点的 free_list 链表对大块内存进行管理,每种节点负责一定大小区间内存块的分配释放的管理。其间还涉及了如何进行碎片内存管理等内容,其缺点是无法对__pool_alloc所管理的内存进行释放。
  • __gnu_cxx::__mt_alloc
    不再进行介绍和解释。

除开以上所讲的③④⑤三种 Class Allocator,还有另两种智能型 Allocator(用处不大):

  • __gnu_cxx::debug_allocator
    该 allocator 是一个外覆器(wrapper),可包覆在任何 allocator 上。其将客户的申请量添加一些,然后由 allocator 回应,并以额外的内存放置 size 信息。一但 deallocate() 收到一个 pointer,就会检查 size 并以 assert() 保证一致;
  • __gnu_cxx::array_allocator
    允许分配一已知固定大小(known and fixed size)的内存块,内存来自 std::array objects。用上这个 allocator, 大小固定的容器(包括 std::string)就无需再调用 ::operator new 和 ::operator delete。这就允许我们使用 STL abstractions 而无需再运行期间添乱、增加开销。甚至在 program startup 情况下也可以使用。
GNU C++ Allocator分类总结与归纳_第3张图片
__gnu_cxx::debug_allocator 实现
GNU C++ Allocator分类总结与归纳_第4张图片
__gnu_cxx::array_allocator 实现

2.2 bitmap_allocator

该分配器的实现使用了以上所说的:用一个索引以 2 的指数倍数成长的桶子(exponentially increasing power-of-two-sized buckets)的思路。先捋一捋bitmap_allocator实现当中的相关数据结构的概念:

  • blocks:即是进行分配的内存区块,每个block size一般取8bytes,是super-block的一部分;
  • super-blocks:表示一个__mini_vector所管理的整个内存空间,bitmap_allocator进行管理内存空间扩容/缩减的操作就是通过增加或减少super-blocks的数目来实现的,是 bitmap_allocator 进行内存管理的基本单位;
  • bitmap:记录一个__mini_vector所管理的内存空间所属的64个 blocks 的分配情况(依据增长情况,不一定是64个,也可能是128/256,block数目是2的幂次增长的),所以为64bit,8bytes(0表示分配出去了,1表示未分配出去;若是全未分配出去则为0x FFFF FFFF FFFF FFFF,若是第一个分配出去则为 0x FFFF FFFF FFFF FFFE(1110),即bitmap字节序从低位到高位表示的是block序号从63到0),是super-block的一部分;
  • mini-vector:该数据结构的实现是为了避免作为容器的分配其还嵌套容器所造成的先有鸡还是先有蛋的困扰,实现过程也即和vector的实现思路相似,见 vector 容器。含有三个成员变量_M_start;(表示所管理的block的起始位置,即block0的内存地址),_M_finish;(表示第一个可分配的block的内存位置),_M_end_of_storage;(类似于vector当中的capacity概念)。其作用是对 super-block 进行存储、访问和管理

具体的数据结构和层次关系见下图所示:
GNU C++ Allocator分类总结与归纳_第5张图片

实际的分配和释放过程如下图:

GNU C++ Allocator分类总结与归纳_第6张图片
1st super-block耗尽,启用 2nd super-block
GNU C++ Allocator分类总结与归纳_第7张图片
2nd super-block耗尽,启用 3rd super-block
GNU C++ Allocator分类总结与归纳_第8张图片
1st super-block 全回收
GNU C++ Allocator分类总结与归纳_第9张图片
2nd super-block 全回收

GNU C++ Allocator分类总结与归纳_第10张图片
          3rd super-block 全回收

其中的实现细节,如分配和释放的具体规则不再赘述,见以上系列图进行归纳整理即可。

你可能感兴趣的:(#,内存管理,#,STL学习,内存管理,c++,allocator,GNU,bitmapallocator)