STL 源码分析六大组件-allocator
1. allocator 基本介绍
分配器(allocator))是C ++标准库的一个组件, 主要用来处理所有给定容器(vector,list,map等)内存的分配和释放。C ++标准库提供了默认使用的通用分配器std::allocator,但是,也可以由程序员自己提供自定义分配器。
2. allocator 标准库规范
我们去看std中的stl分配器实现,会发现无论你的实现思路怎么变,所有模板类中的接口和成员变量都是一样的,那么这是因为C++标准库在制定分配器时,是有提出硬性标准的,具体接口和成员变量如下:
STD 标准规范(GNU ISO C++ Library 5.2.1):
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
void construct(pointer __p, const _Tp& __val) { ::new((void *)__p) value_type(__val); }
void destroy(pointer __p) { __p->~_Tp(); }
size_type max_size() const _GLIBCXX_USE_NOEXCEPT { return size_t(-1) / sizeof(_Tp); }
address(const_reference __x) const _GLIBCXX_NOEXCEPT
deallocate(pointer, size_type);
allocate(size_type __n, const void* = 0);
template<typename _Tp1>
struct rebind { typedef allocator<_Tp1> other; };
对于上面这一段接口和变量,其中一些在其他容器的分析中,我们会经常看到,下面对于其中有疑问的点我们来解释一下:
size_type:
为 unsigned 类型 , 表示容器中元素长度或者下标,例:vector::size_type i = 0;我们都知道 size_t 在32位和64位系统上是不一样的,size_t已经可以解决平台差异了,那为什么还要引入size_type,这里我们可以理解为size_t是属于全局的,而size_type是跟容器相关的,是属于STL的一套,在其他容器中也是一样的。
difference_type:
为 signed 类型 , 表示迭代器差距, vector:: difference_type = iter1-iter2,ptrdiff_t通常用来保存两个指针减法操作的结果。
size_t(-1):
size_t 为 unsigned 类型,传入 -1 获得size_t该系统的最大值。
3. GNU ISO C++ Library 中STL分配器实现
1. 默认分配器:
按照标准规范,标准库中stl实现的分配器,对外接口,成员变量几乎都一样,只是接口内部实现有区别,那么我们看一下具体实现流程,
首先看一下容器默认的分配器std::allocator。 看代码我们会发现,默认分配器真正的实现是在new_allocator.h头文件中做的,头文件具体调用顺序为:allocator.h -> c++allocator.h -> new_allocator.h, 看代码我们知道,在前面两个头文件中基本没做什么,真正的实现为模板类new_allocator。 关键代码如下所示:
namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Tp>
class new_allocator
{
public:
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
// NB: __n is permitted to be 0. The C++ standard says nothing
// about what the return value is when __n == 0.
pointer
allocate(size_type __n, const void* = 0)
{
if (__n > this->max_size())
std::__throw_bad_alloc();
return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
}
// __p is not permitted to be a null pointer.
void
deallocate(pointer __p, size_type)
{ ::operator delete(__p); }
}
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
我们看一下分配和释放的接口allocate和deallocate,实现上只是单纯的将::operator new和::operator delete进行了一下封装,没用做特殊处理。
当然,如果我们不特别说明,直接在Linux系统下使用标准C++的stl容器,在为容器元素分配内存时,使用的方式就是类似于正常的new和delete,这种方式对于不频繁分配,并且一次分配大块内存的使用情况是适用的,典型的容器像vector和deque。
2. 拓展分配器:
我们继续查看STL源码会发现,除了默认分配器,gnu下的STL还实现了一些特殊的分配器,像__pool_alloc,__mt_alloc,array_allocator,malloc_allocator (在源码的/c++/ext/ 目录下)
__pool_alloc :比较出名的SGI内存池分配器,侯捷老师在STL源码剖析中着重分析的,后面会专门加一章来进行讲解
__mt_alloc : 多线程内存池分配器,具体可以看一下该博主的链接讲解,还是比较详细的
array_allocator : 全局内存分配,只分配不释放,交给系统来释放
malloc_allocator : 封装了一下std::malloc和std::free
4. 自定义分配器 (allocator)
我们为什么要自定义分配器,废话,主要原因当然是因为性能了。利用自定义分配器可以显著改善程序性能及分配器使用便利性。
对于默认分配器,也就是我们常使用的new和malloc,如果遇到需要频繁分配小块内存对象的情况,因为需要不停的向系统锁要内存,那么将使得分配过程变得很慢;还有就是导致出现过多的内存碎片;以及一些极端情况,分配的内存过于小且数量庞大,导致分配一次内存,附带的内存消耗反而更大,造成内存的浪费
主流提高性能的方式是创建基于内存池的分配器,每次在容器中插入删除元素,不是分配内存,而是分配程序启动时预先分配的大块内存(内存池)。这种自定义分配器,通过简单的从池中返回指向内存的指针来提供单独的分配请求。还有就是可以推迟实践的内存释放,直到内存池生命周期结束
C++之父 Bjarne stroustrup 提到,自定义分配器的三个主要应用,即内存池分配器,共享内存分配器和垃圾收集(gc)分配器
垃圾回收应该就是智能指针,自己控制资源的回收;内存池则主要负责对性能的提升;共享内存分配器则是对特殊缓存区的处理。 后续会一一分析介绍!
具体链接:
SGI 内存池分配器
智能指针
共享内存分配-hashmap
多线程分配器
2018年9月8日00:18:28
STL 源码分析六大组件-allocator的更多相关文章
- STL源码分析读书笔记--第二章--空间配置器(allocator)
声明:侯捷先生的STL源码剖析第二章个人感觉讲得蛮乱的,而且跟第三章有关,建议看完第三章再看第二章,网上有人上传了一篇读书笔记,觉得这个读书笔记的内容和编排还不错,我的这篇总结基本就延续了该读书笔记的 ...
- STL源码分析《4》----Traits技术
在 STL 源码中,到处可见 Traits 的身影,其实 Traits 不是一种语法,更确切地说是一种技术. STL库中,有一个函数叫做 advance, 用来将某个迭代器(具有指针行为的一种 cla ...
- STL源码分析《3》----辅助空间不足时,如何进行归并排序
两个连在一起的序列 [first, middle) 和 [middle, last) 都已经排序, 归并排序最核心的算法就是 将 [first, middle) 和 [middle, last) 在 ...
- STL 源码分析《1》---- list 归并排序的 迭代版本, 神奇的 STL list sort
最近在看 侯捷的 STL源码分析,发现了以下的这个list 排序算法,乍眼看去,实在难以看出它是归并排序. 平常大家写归并排序,通常写的是 递归版本..为了效率的考虑,STL库 给出了如下的 归并排序 ...
- stl源码分析之allocator
allocator封装了stl标准程序库的内存管理系统,标准库的string,容器,算法和部分iostream都是通过allocator分配和释放内存的.标准库的组件有一个参数指定使用的allocat ...
- STL源码分析与实现-stl_list容器
1. stl_list 介绍 今天我们来总结一下stl_List, 通过之前介绍单链表的文章,其实对链表的基本操作已经十分熟悉了,那对于stl_list,无非就是链表结构不一样,至于其中的增删改查的细 ...
- STL 源码分析《2》----nth_element() 使用与源码分析
Select 问题: 在一个无序的数组中 找到第 n 大的元素. 思路 1: 排序,O(NlgN) 思路 2: 利用快排的 RandomizedPartition(), 平均复杂度是 O(N) 思路 ...
- 精尽Spring MVC源码分析 - MultipartResolver 组件
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerMapping 组件(一)之 AbstractHandlerMapping
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
随机推荐
- 详细讲解:零知识证明 之 ZCash 完整的匿名交易流程
作者:林冠宏 / 指尖下的幽灵 博客:http://www.cnblogs.com/linguanh/ 掘金:https://juejin.im/user/587f0dfe128fe100570ce2 ...
- git status -s 的表达式的读法
1 2 读法 解决方案 ?? 未被追踪 git add -A 或git add 文件路径 A 新添加文件 注:??被add后的状态 OK,等待commit. M 已 ...
- Java简单高精度合集
第一个Java的算法程序.记得可以使用Alt+'/'自动补全sysout和main之类的. BigInteger在java.math.BigInteger中. import java.math.Big ...
- POJ1700 【经典过河问题,贪心】
题意: n个人过河, 船每次只能坐两个人, 然后船载每个人过河的所需时间不同, 问最快的过河时间. 思路: 仅仅启发一下思维: 我相信很多人一下子的想法就会有,每次最快和那些慢的过去,然后让最快一直来 ...
- bzoj 1058: [ZJOI2007]报表统计【set】
我想写FHQtreap的!是set自己跑进代码的!因为太好写了 是有点慢--洛谷上不吸氧会T一个点 就是,用一个set p维护所有点值,ans维护MIN_SORT_GAP的答案,每次insert一个点 ...
- NOIp2016 蚯蚓 【二叉堆/答案单调性】By cellur925
题目传送门 $Sol$ $50pts$:我们考虑$q==0$的情况,每次在所有的蚯蚓中找到一只长度最大的,这非常二叉堆.所以我们可以用一个优先队列,随便水一下就有50分.($NOIp$的分真这么好拿? ...
- USACO Training3.3亚瑟王的宫殿【搜索】By cellur925
题目传送门 因为太蒟了,所以参考了dalao@zbtrs == 对此表示感谢并侵删. 看起来我们就知道这是搜索题. 最后的情况分两种:有骑士背国王/国王自食其力走到集合点. 首先,我们不知道大家 ...
- 洛谷 P3312 [SDOI2014]数表
式子化出来是$\sum_{T=1}^m{\lfloor}\frac{n}{T}{\rfloor}{\lfloor}\frac{m}{T}{\rfloor}\sum_{k|T}\mu(\frac{T}{ ...
- Input对象2(貌似是独立标签)
6.hidden隐藏域的样式表单:Hidden属性可以创建一个隐藏域,数据会被隐藏起来,因此用户是无法操作的.这样说来hidden好像没有什么用,事实上,正是出于安全的考虑,在多步操作数据的同时,用h ...
- AJPFX关于Set接口学习笔记及总结
Set接口中的方法和Collection中方法一致的.Set接口取出方式只有一种,迭代器. |--HashSet:底层数据结构是哈希表,线程是不同步的.无序,高效: HashSet集合保证元素唯一性: ...