空间配置器(allocator)详解-stl源码剖析学习笔记

2023-11-05

一、什么是空间配置器

空间配置器也就是配置空间,配置容器所需要的空间,该空间获取可以是内存,也可以是磁盘或其他存储介质。

二、STL规范必要接口

stl有很多实现版本,根据stl规范,allocator的必要接口如下:

//类型型别,设计缘由后续章节会介绍
allocator::value_type
allocator::pointer
allocator::const_pointer
allocator::reference
allocator::const_reference
allocator::size_type
allocator::difference_type
//一个嵌套的模板类。class rebind<U>拥有唯一成员other,一个allocator<U>的typedef类型定义
allocator::rebind
//默认构造函数
allocator::allocator()
//拷贝构造函数
allocator::allocator(const allocator&)
//泛化的拷贝构造函数
template<class U> allocator::allocator(const allocator<U>&)
//默认析构函数
allocator::~allocator()
//返回某个对象的地址。算式a.address(x)等同于&x
pointer allocator::address(reference x) const
//返回某个const对象地址。算式a.address(x)等同于&x
//配置空间,足以存储n个T对象。第二个参数是个提示。实现上可能会利用它来增进区域性,或可完全忽略
pointer allocator::allocate(size_type n, const void* = 0)
//归还先前配置的空间
void allocator::deallocate(pointer p, size_type n)
//返回可成功配置的最大量
size_type allocator::max_size() const
//等同于new((const void*) p) T(x)
void allocator::construct(pointer p, const T& x)
//等同于p->~T()
void allocator::destroy(pointer p)

SGI STL的实现版本的空间配置有一个符合部分标准的空间配置std::allocator,该配置器只对::operator new和::operator delete进行了薄薄的封装,另有一个特殊空间配置器std::alloc进行复杂的内存动态配置和释放。以下我们讲SGI STL的空间配置。

三、配置器构造、析构部分

一般我们所习惯的C++内存配置操作和释放操作是这样的:

class Foo {};
Foo* pf = new Foo();	//配置内存,然后构造对象
delete pf;				//将对象析构,然后释放内存 

其中new算式包含两阶段操作:
1>调用::operator new配置内存
2>调用Foo::Foo()构造对象内容
delete算式也包含两阶段内容:
1>调用Foo::~Foo()析构对象
2>调用::operator delete释放内存

为了精密分工,stl allocator将这两个阶段区分开来。内存配置操作由alloc::allocate()负责,内存释放由alloc::deallocate()负责,对象构造由::construct()负责,对象析构由::destroy()负责。

stl配置器定义于中,SGI包含了以下两个文件:
#include<stl_alloc.h> //负责内存空间的配置和释放
#include<stl_construct.h> //负责对象内容的构造和析构

下图为的包含文件以及定义的内容:
在这里插入图片描述
<stl_construct.h>中的construct()实现:

#include<new.h>	//欲使用placement new,先包含此文件

template<class T1, class T2>
inline void constuct(T1* p, const T2& value) {
	new(p) T1(value);	//placement new,调用T1::T1(value);
}

placement new:operator new的一个重载版本,不能自定义,分配内存空间在一个指定的已知空间,对应语法结构为A *p = new(ptr) A;次步骤为在ptr上分配空间,然后调用A的构造函数生成对象,p与ptr所指向的地址相同。此处为调用T1::T1(value),在指定地址p所指空间上设置初始值value。

destroy()实现:

//destroy第二个版本,接受两个迭代器,value_type找出数值型别
template<class ForwardIterator>
inline void destroy(ForwardIterator  first, ForwardIterator last) {
	__destroy(first, last, value_type(first));
}
//判断value_type是否有trivial destructor
template<class ForwardIterator, class T>
inline void __destroy(ForwardIterator  first, ForwardIterator last, T*) {
	typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
	__destroy_aux(first, last, trivial_destructor());
}
template<class ForwardIterator>
inline void __destroy_aux(ForwardIterator  first, ForwardIterator last, __false_type) {
	for (; first < last; ++first)
		destroy(&*first);
}
template<class ForwardIterator>
inline void __destroy_aux(ForwardIterator  first, ForwardIterator last, __true_type) {}

destroy()第一个版本直接调用析构函数,第二个版本会获取迭代器所指之物的型别,再利用_type_traits判断是否为trivial即是否无关痛痒,如果每个对象的析构函数操作都无关痛痒,则什么都不做,较少效率伤害,如果为_false_type则循环访问整个迭代器范围,并每次调用第一版本的destroy()。

四、空间的配置和释放

空间的配置和释放在<stl_alloc.h>中,考虑到小型区块可能造成的内存破碎问题,采用双层级配置器结构,利用宏定义_USE_MALLOC决定开放一级配置器,或者同时开放二级配置器。实际可以测试,SGI STL并未定义该宏。一二级配置结构如下:
在这里插入图片描述

1> 第一级配置

第一级配置以malloc(),free(),realloc()来实现配置,释放,再配置,如果配置需求不能被满足,则会循环调用自设定的模拟set_new_handler()并尝试再次配置,企图会在一次再次配置时成功。

2> 第二级配置

第二级配置避免了太多小额区块造成内存碎片以及其带来的管理内存的额外空间负担。其做法是区块够大交给第一级配置,区块小于128bytes交由内存池管理。

整个过程是怎么操作的呢?

第二级配置共维护16个free_lists,各自管理大小为8的倍数的小额区块,分别为
8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128bytes的小额区块。每次有内存需求,就从free_lists中拨出,如果客端释还小额区块,则回收到free_lists中。每次分配的需求量配置器会主动上调至8的倍数。其维护的函数有配置函数allocate(),释放函数deallocate(),以及free_list不可用时的填充函数refill()。

函数allocate()的实现过程:判断区块大小n是否大于128bytes,大于调用一级函数malloc_alloc::allocate(n),不大于则寻找适当的free_list,比如n为96bytes,对应的第12个free_list,如果该free_list有可用则拨出,调整剩余free_list的指向,如果没有则调用refill()填充free_list。图示如下:
在这里插入图片描述
释放函数deallocate(),同样先判断区块大小是否大于128,大于则调用一级配置malloc_alloc::deallocate(p,n),不大于则找到对应的free_list进行回收。图示如下:
在这里插入图片描述
填充函数refill(),free_list没有可用空间时,将从内存池取空间(chunk_alloc()函数完成)并将结果区块依次连接到free_list进行填充。

chunk_alloc()函数,以end_free-start_free来判断内存池水量是否充足,如果充足则返回20个区块(默认取20个)给free_list,如果不充足但足够一个以上的区块,则把这些不足的返回给free_list,并调整nobjs为实际区块数,如果一个都不够,则利用malloc从heap中获取,新水量为获取量的两倍和一个随配置次数增加的附加量,heap成功,则递归调用自己,调整nobjs,按刚才策略返回给free_list,剩余留给内存池,如果heap失败,则遍历free_lists寻找是否有剩余的尚未使用且区块够大的空间,找到了就挖一块交出,找不到就调用一级配置,因为它有类似的new_handler机制,尝试看是否还能释放出内存,否则就发出bad_alloc异常。
以上为第二级空间配置器的设计内容。

内存池分配示例:
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

空间配置器(allocator)详解-stl源码剖析学习笔记 的相关文章

  • 如何获取正在访问 ASP.NET 应用程序的当前用户?

    为了获取系统中当前登录的用户 我使用以下代码 string opl System Security Principal WindowsIdentity GetCurrent Name ToString 我正在开发一个 ASP NET 应用程
  • 为什么 C# Array.BinarySearch 这么快?

    我已经实施了一个很简单用于在整数数组中查找整数的 C 中的 binarySearch 实现 二分查找 static int binarySearch int arr int i int low 0 high arr Length 1 mid
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • 动态加载程序集的应用程序配置

    我正在尝试将模块动态加载到我的应用程序中 但我想为每个模块指定单独的 app config 文件 假设我的主应用程序有以下 app config 设置
  • 查找c中结构元素的偏移量

    struct a struct b int i float j x struct c int k float l y z 谁能解释一下如何找到偏移量int k这样我们就可以找到地址int i Use offsetof 找到从开始处的偏移量z
  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • 嵌套接口:将 IDictionary> 转换为 IDictionary>?

    我认为投射一个相当简单IDictionary
  • 类模板参数推导 - clang 和 gcc 不同

    下面的代码使用 gcc 编译 但不使用 clang 编译 https godbolt org z ttqGuL template
  • Clang 3.1 + libc++ 编译错误

    我已经构建并安装了 在前缀下 alt LLVM Clang trunk 2012 年 4 月 23 日 在 Ubuntu 12 04 上成功使用 GCC 4 6 然后使用此 Clang 构建的 libc 当我想使用它时我必须同时提供 lc
  • 创建链表而不将节点声明为指针

    我已经在谷歌和一些教科书上搜索了很长一段时间 我似乎无法理解为什么在构建链表时 节点需要是指针 例如 如果我有一个节点定义为 typedef struct Node int value struct Node next Node 为什么为了
  • while 循环中的 scanf

    在这段代码中 scanf只工作一次 我究竟做错了什么 include
  • 控件的命名约定[重复]

    这个问题在这里已经有答案了 Microsoft 在其网站上提供了命名指南 here http msdn microsoft com en us library xzf533w0 VS 71 aspx 我还有 框架设计指南 一书 我找不到有关
  • 覆盖子类中的字段或属性

    我有一个抽象基类 我想声明一个字段或属性 该字段或属性在从该父类继承的每个类中具有不同的值 我想在基类中定义它 以便我可以在基类方法中引用它 例如覆盖 ToString 来表示 此对象的类型为 property field 我有三种方法可以
  • WPF/C# 将自定义对象列表数据绑定到列表框?

    我在将自定义对象列表的数据绑定到ListBox in WPF 这是自定义对象 public class FileItem public string Name get set public string Path get set 这是列表
  • 如何从两个不同的项目中获取文件夹的相对路径

    我有两个项目和一个共享库 用于从此文件夹加载图像 C MainProject Project1 Images 项目1的文件夹 C MainProject Project1 Files Bin x86 Debug 其中有project1 ex
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • C# 成员变量继承

    我对 C 有点陌生 但我在编程方面有相当广泛的背景 我想做的事情 为游戏定义不同的 MapTiles 我已经像这样定义了 MapTile 基类 public class MapTile public Texture2D texture pu
  • 如何将服务器服务连接到 Dynamics Online

    我正在修改内部管理应用程序以连接到我们的在线托管 Dynamics 2016 实例 根据一些在线教程 我一直在使用OrganizationServiceProxy out of Microsoft Xrm Sdk Client来自 SDK
  • 如何防止用户控件表单在 C# 中处理键盘输入(箭头键)

    我的用户控件包含其他可以选择的控件 我想实现使用箭头键导航子控件的方法 问题是家长控制拦截箭头键并使用它来滚动其视图什么是我想避免的事情 我想自己解决控制内容的导航问题 我如何控制由箭头键引起的标准行为 提前致谢 MTH 这通常是通过重写
  • 对来自流读取器的过滤数据执行小计

    编辑问题未得到解答 我有一个基于 1 个标准的过滤输出 前 3 个数字是 110 210 或 310 给出 3 个不同的组 从流阅读器控制台 问题已编辑 因为第一个答案是我给出的具体示例的字面解决方案 我使用的实际字符串长度为 450 个

随机推荐

  • Puppeteer基础入门、常见应用、利用谷歌插件编写Puppeteer脚本

    前言 Puppeteer已经听说过很多次了 也见过一些与之相关的文章 但是一直没怎么研究过 现在来简单学习一下 简介 Puppeteer 是一个 Node 库 它提供了一个高级 API 来通过 DevTools 协议控制 Chromium
  • 前端学习——html

    1 页面标签包含在里 其中有头和躯干 一 head里的常用标签设置 meta标签的设置 在网页中 meta标签最常用的设置是用来设置字符集
  • 静态和动态类型编程语言的区别

    静态和动态是针对变量的数据类型而言的 区别如下 1 使用静态类型语言编写的代码中 要声明变量的数据类型 而且不同数据类型的变量不允许直接赋值 它的数据类型是编译期间进行检查的 2 静态类型语言在使用变量之前 需要为它们分配好内存 3 静态类
  • python画折线图两种写法

    import matplotlib pyplot as plt from openpyxl import load workbook 这个是从Excel表格中导入数据 为了让中文不显示成乱码 plt rcParams font sans s
  • java中锁的面试题

    1 synchronized锁 悲观锁 同步锁 synchronized关键字 表示 同步 的 它可以对 多行代码 进行 同步 将多行代码当成是一个完整的整体 一个线程如果进入到这个代码块中 会全部执行完毕 执行结束后 其它线程才会执行 这
  • Linux学习整理-网络命令集

    目录 前提 1 机器IP地址查询 1 1 ifconfig 1 1 1 安装包 1 1 2 执行命令 1 1 3 拓展 ifconfig的其它用法 1 1 4 常用的属性说明 1 2 ip addr 1 2 1 查看IP地址 1 2 2 其
  • 【实战】区块链技术如何应用于金融领域?

    信任是金融业的基础 为维护信任 金融业的发展催生了大量的中介机构 包括托管机构 第三方支付平台 公证人 银行等 然而 中介机构处理信息依赖人工 且交易信息往往需要经过多道中介的传递 这使得信息出错率高 且效率低下 同时 人们也通常认为权威机
  • Python进阶系列:(八)python反射

    文章目录 前言 一 反射 总结 前言 Python系列文章主要是记录自己学习成果及知识输出整合 提供一个温故而知新的场所 一 反射 1 什么是反射 把字符串映射到实例的变量或实例的方法 只通过字符串调用类中的变量或方法 反射的本质 核心 利
  • 20230830—图形设计

    include app h include
  • C++(函数重载和函数模板)

    重载和模板 一 函数重载 1 函数重载定义 2 判断函数重载的规则 2 名字粉碎 名字修饰 3 C 编译时函数名修饰约定规则 4 C 函数是重载 二 函数模板 一 函数重载 1 函数重载定义 在C 中可以为两个或两个以上的函数提供相同的函数
  • 十三、断路器-Hystrix 的隔离策略

    版权声明 本文为博主原创文章 未经博主允许不得转载 https blog csdn net dengqiang123456 article details 75935122 说明 1 Hystrix 通过舱壁模式来隔离限制依赖的并发量和阻塞
  • 探索创意之旅:打造个人网页的精彩奇遇

    在茫茫的网络世界里 我找到了一个属于自己的小天地 那里不仅有我独特的创意 还有我内心深处的声音 我的个人网页是一段关于探索创意之旅的故事 让我带你一窥我在这个奇妙旅程中的所见所闻 声明 这个网页是使用React18 x写的 由于我平常都是使
  • MATLAB 画常见二次曲面汇总

    一 螺旋线 1 静态螺旋线 a 0 0 1 20 pi h plot3 a cos a a sin a 2 a b linewidth 2 axis 50 50 50 50 0 150 grid on set h erasemode non
  • netbeans的UI代码重新打开可视化视图

    netbeans重新打开可视化视图 视图 gt 编辑器 gt 设计
  • AJAX同步和异步

    1 AJAX 简介 1 1同步和异步 一 同步与异步 1 同步 顺序执行 优点 静态预判结果可控 缺点 耗时任务阻塞执行 2 异步 乱序执行 优点 不会阻塞代码 体验好 缺点 顺序不可控 以银行排队办业务为例 1 同步 默认排队叫号 依次办
  • [LeetCode] 01矩阵中最大矩形 Maximal Rectangle

    相关问题1 LeetCode Find max subsquare whose border values are all 1 相关问题2 LeetCode 01矩阵中最大正方形 Maximal Square Given a 2D bina
  • 微信小程序自定义键盘

    效果图 功能 如果输入 直接补0 如果是09 直接是9 如果是000那就有一个0 不能大于6位 小数点不能大于两位仅能出现一次 还有不输入是禁止支付的 不能小于0 01 失去焦点隐藏面板 光标问题有点小bug 望大佬指点 完整代码 wxml
  • react antd 实现 表格(Table)多个多选功能组件实现

    壹 功能展示和使用需求 需求描述 基于antd 实现 表格要实现多个多选互不影响包含 全选 半选 可自由拓展 功能展示 贰 封装代码 import Checkbox Table from antd import React useEffec
  • 数据挖掘中的机器学习

    1 机器学习的核心目标 从经验数据中推导出规律 学习 机器从经验数据中推导并找出规律的过程 预测 将规律应用于新数据的过程 模型 其中的规律 2 机器学习处理的问题分为监督学习和无监督学习 监督学习又可分为分类 离散 与回归 连续 3 人学
  • 空间配置器(allocator)详解-stl源码剖析学习笔记

    一 什么是空间配置器 空间配置器也就是配置空间 配置容器所需要的空间 该空间获取可以是内存 也可以是磁盘或其他存储介质 二 STL规范必要接口 stl有很多实现版本 根据stl规范 allocator的必要接口如下 类型型别 设计缘由后续章