当前位置: 首页 > news >正文

STL-空间配置器的了解

前言

        空间配置器,顾名思义就是为了各个容器高效的管理空间(空间的申请与回收)的,在默默的工作的。虽然在常规上使用STL时,可能用不上它,但是站在学习研究的角度,学习它的实现原理对我们有很大的帮助。

目录

1.为什么需要空间配置器

2.SGI—STL空间配置器实现原理

3.一级空间配置器实现原理

        3.1 一级空间配置器

        3.2 二级空间配置器

                3.2.1内存池

                3.2.2 SGI-STL中二级空间配置器设计

        3.3 SGI-STL二级空间配置器之空间申请

                3.3.1前期的准备

                3.3.2申请空间

                        3.3.2.1填充内存块

                        3.3.2.2向内存池中索要空间

                3.3.3空间的回收

        3.4 空间配置器的默认选择

        3.5 空间配置器的再次封装

        3.6 对象的构造和释放

        


1.为什么需要空间配置器

        new申请的空间,虽然可以使用,但是有以下不足之处:

        1.空间申请与释放需要用户自己管理,容易造成内存泄露 

        2.频繁向系统申请小块内存块,容易造成内存碎片

        3.频繁向系统系统申请小块内存,影响程序运行效率

        4.直接使用malloc和new进行申请每块空间前有额外空间浪费

        5.申请失败如何应对

        6.代码结构比较混乱,代码复用率不高

        7.未考虑线程安全问题

        由于存在这些问题,因此需要一块高效的的内存管理机制。 

2.SGI—STL空间配置器实现原理

        频繁的向系统申请小块内存会有很多的问题,那么怎么样的内存算是小块内存呢?

        SGI-STL以128作为大内存和小内存的分界线,将空间配置器分为两级结构,一级空间配置器处理大块内存,二级空间配置器处理小块内存。 

3.一级空间配置器实现原理

        3.1 一级空间配置器

        一级空间配置器的原理非常简单,直接对malloc和free进行了封装,并增加了C++中set_new_handle思想。

namespace qyy
{template <int inst>class __malloc_alloc_template{private:static void* oom_malloc(size_t);public://对malloc进行封装static void* allocate(size_t n){//申请空间成功直接返回,失败了交给oom_malloc处理void* result = malloc(n);if (result == 0){result = oom_malloc(n);}return result;}//对free进行封装static void* deallocate(void* p, size_t){free(p);}//模拟实现set_malloc_handler//该函数参数为函数指针,返回值也是函数指针//void(*set_malloc_handler(void*(f*)( )  )) ()static void (*set_malloc_hander(void (*f)()))(){void (*old)() = __malloc_alloc_oom_handler;__malloc_alloc_oom_handler = f;return (old);}};//malloc空间申请失败时调用该函数template<int inst>void* __malloc_alloc_template<inst>::oom_malloc(size_t){void(*my_malloc_handler)();void* result;for (;;){//检测用户是否设置空间不足应对措施,如果没有设置,抛异常模拟new的方式my_malloc_handler = __malloc_alloc_handler;if (0 == my_malloc_handler){__THROW_BAD_ALLOC;}//如果设置了,执行用户提供空间不足应对措施(*my_malloc_handler)();//继续申请空间可能就会成功result = malloc(n);if (result)return (result);}}typedef __malloc_alloc_template<0> malloc_alloc;}

        3.2 二级空间配置器

        二级空间配置器是专门负责处理小于128字节的小块空间。如何才能提升小块内存的的申请和释放的方式呢?SGI-STL采用了内存池的技术来提高申请空间的速度以及减少额外的空间的浪费,采用哈希桶的方式来提高用户获取空间的速度与高效管理。 

                3.2.1内存池

        内存池就是:先申请一块比较大的内存当做备用,当需要内存时,直接去内存池中拿内存就好了,当内存池中的空间不够时,再向内存中取,当用户不用时,直接还回内存池即可。避免了频繁的向系统申请小块内存所造成的效率低,内存碎片以及额外浪费的问题。 

                3.2.2 SGI-STL中二级空间配置器设计

        SGI-STL中的二级空间配置器使用了内存池技术,但是没有采用链表的方式对用户已归还的空间进行管理(因为用户申请空间时是在查找合适的小块内存时效率较低),而采取哈系桶的方式进行管理。那么是否128个桶来管理已归还的空间呢?答案是不需要的,因为用户申请空间基本上都是按照4的整数倍来申请的,其他大小的空间基本很少用到,因此:SGI-STL将用户申请的内存块向上对其到了8的整数倍。  

        3.3 SGI-STL二级空间配置器之空间申请

                3.3.1前期的准备

namespace qyy
{template<int inst>class __default_alloc_template{private:enum {__ALIGN = 8};//如果用户需要的内存不是8的整数倍,向上对其到8的整数倍enum {__MAX_BYTES = 128};//大小块内存的分界线enum { __NFREELISTS = __MAX_BYTES / __ALIGN };//采用哈希桶保存小块内存时所需要的内存个数static size_t ROUND_UP(size_t bytes){return (((bytes)+__ALIGN - 1) & ~(ALIGN - 1));}private://用联合图来维护链表结构union obj{union obj* free_list_link;char client_data[1];};private:static obj* free_list[__NFREELISTS];//哈希函数根据用户提供的字节数找到对应的桶号static size_t FREELIST_INDEX(size_t bytes){return (((bytes)+__ALIGN - 1) / ALING - 1);}//start_free 和end_free用来标记内存池中大块内存的起始和末尾位置static char* start_free;static char* end_free;//用来记录该空间配置器已经向系统索要了多少的块内存static size_t heap_size;///...};
}

                3.3.2申请空间

//函数功能:向空间配置器索要空间//参数n:所需要的空间字节数//返回值:返回空间的首地址static void* allocate(size_t n){obj* __VOLATILE* my_free_list;obj* __RESTRICT result;//检测用户所需要的空间是否在超过128字节(即是否为小块内存)if (n > (size_t)__MAX_BYTES){//不是小块内存交给一级空间配置器来处理return (malloc_alloc::allocate(n));}//根据用户所需要的字节找到对应的桶号my_free_list = free_list + FREELIST_INDEX(n);result = *my_free_list;//如果桶里面没有内存块,向桶里面补充内存块if (result == 0){//n将向上对齐到8的整数倍,保证向桶里面补充内存块时,内存块一定是8的整数倍void* r = refill(ROUND_UP(n));return r;}//维护桶里面剩余内存块的链式关系*my_free_list = result->free_lsit_link;return (result);}

                        3.3.2.1填充内存块

		//函数功能:向哈系桶里面补充空间//参数n小块内存//首个小块内存的首地址template<int inst>void* __default_alloc_template<inst>::refill(size_t n){//一次性向内存池索要20个n字节的空间int nobjs = 20;char* chunk = chunk_alloc(n, nobjs);obj** my_free_list;obj* result;obj* current_obj, * next_obj;int i;//如果只有一块直接返回给用户if (1 == nobjs)return (chunk);//找到对应的桶号my_free_list = free_list + FREELIST_INDEX(n);//将第一块返回给用户,去其他的挂到哈系桶中result = (obj*)chunk;*my_free_list = next_obj = (obj*)(chunk + n);for (i = 1;;i++){current_obj = next_obj;next_obj = (obj*)((char*)next_obj + n);if (nobjs - 1 == i){current_obj->free_list_link = 0;break;}else{current_obj->free_list_link = next_obj;}}return (result);}

                        3.3.2.2向内存池中索要空间

 

	    template<int inst>char* __default_alloc_tempalte<inst>::chunk_alloc(size_t size, int& nobjs){//计算nobjs个size字节内存块的总大小以及内存池中剩余空间的总大小char* result;size_t total_bytes = size * nobjs;size_t bytes_left = end_free - start_free;//如果内存池可以提供total_bytes字节,返回if (bytes_left >= tatal_bytes){result = start_free;start_free += tatal_bytes;return (result);}else if (bytes_left >= size){//nobjs块无法提供,但是至少可以提供1块size字节的内存块,提供后返回nobjs = bytes_left / size;tatal_byte = size * nobjs;result = start_free;start_free += tatal_bytes;return (result);}else{//内存池空间不够连一小块内存都没有。//向系统堆求助,往内存池中补充空间//计算向内存中补充空间的大小:本次空间总大小的两倍 + 向系统申请总大小/16size_t bytes_to_get = 2 * tatal_byte + ROUND_UP(heap_size >> 4);//如果内存池有剩余空间(该空间一定是8的整数倍)将该空间挂到对应的哈希桶中if (bytes_left > 0){//找到对应的哈系桶obj** my_free_list = free_list + FREELIST_INDEX(bytes_left);((obj*)start_free)->free_list_link = *my_free_list; )* my_ree_list = (obj*)start_free;}//通过系统堆向内存池补充空间,如果补充成功,递归继续分配start_free = (char*)malloc(bytes_to_get);if (0 == start_free){//通过系统堆补充失败,在哈系桶中寻找是否有没有使用的较大块内存int i;obj** my_free_list, * p;for (i = size; i <= __MAX_BYTES;i += __ALIGN){my_free_list = free_list + FREELIST_INDEX(i);p = *my_free_list;//如果有,将该内存块补充进内存池,递归继续分配if (0 != p){*my_free_list = p->free_list_link;start_free = (char*)p;end_free = start_free + i;return (chunk_alloc(size, nobjs));}}//山穷水尽,只能向一级空间配置器求助//注意:此处一定要将end_free置空,因为一级空间配置器一旦抛异常就会出问题end_free = 0;start_free = (char*)malloc_alloc::allocate(bytes_to_get);}heap_size += bytes_to_get;end_free = start_free + bytes_to_get;return (chunk_alloc(size, nobjs));}}

                3.3.3空间的回收

		//函数功能:用户将空间归还给空间配置器//参数:p空间首地址 n空间总大小static void deallocate(void* p, size_t n){obj* q = (obj*)p;obj** my_free_list;if (n > (size_t)__MAX_BYTES){malloc_alloc::deallocate(p, n);return;}//找到对应的哈系桶,将内存挂到哈系桶中my_free_list=free_list+ FREELIST_INDEX(n);q->free_list_link = *my_free_list;*my_free_list = q;}

        3.4 空间配置器的默认选择

        SIG_STL默认使用一级空间配置器 还是二级空间配置器,是通过宏开关USE_MALLOC宏进行控制的。

         在SGI_STL中没有定义这个宏所以,默认情况下SGI_STL使用二级空间配置器。

        3.5 空间配置器的再次封装

        在C++中,用户所需要的空间可能是任意类型的,有单个对象的空间,有连续空间,每一次都让用户自己计算空间的总大小不是很友好,因此SGI_STL将空间配置器重新封装了一层。

template<class T,class ALLOC>class simlpe_alloc{public://申请n个T类型对象大小的空间static T* allocate(size_t n){return 0 == n ? 0 : (T*)ALLOC::allocte(n * sizeof(T));}//申请一个T类型对象大小的空间static T* allocate(void){return (T*)ALLOC::allocate(sizeof(T));}//释放n个T类型对象大小的空间static void deallocate(T* p, size_t n){if (0 != n){ALLOC::deallocate(p, n * sizeof(T));}}static void deallocate(T* p){ALLOC::deallocate(p, sizeof(T));}

 

        3.6 对象的构造和释放

        因为效率的需要,SGI_STL决定将空间的申请和释放和对象的构造析构两个过程分离,因为有些对象的构造不需要调用构造函数,销毁时不需要调用析构函数,将该过程分离可以提高程序的性能。

	//归还空间时,先调用析构函数进行资源的清理template <class T>inline void destory(T* pointer){pointer->~T();}//空间申请好后,调用该函数:利用placement-new完成对对象的构造template<class T1,class T2>inline void construct(T1* p, const T2& value){new(p)T1(value);}

         注意,在释放对象时,需要根据对象的类型来确定是否需要调用析构函数(类型萃取)

        对象的类型可以通过迭代器萃取到。

        这些内容难度较大作为了解就可以了。(对于初学者来说) 

 

        

相关文章:

STL-空间配置器的了解

前言 空间配置器&#xff0c;顾名思义就是为了各个容器高效的管理空间&#xff08;空间的申请与回收&#xff09;的&#xff0c;在默默的工作的。虽然在常规上使用STL时&#xff0c;可能用不上它&#xff0c;但是站在学习研究的角度&#xff0c;学习它的实现原理对我们有很大的…...

哔哩哔哩 B站 bilibili 视频视频音效调节 清澈人声

视频音效调节方式&#xff1a;直接视频播放内容界面内鼠标右键点击视频音效调节 注意&#xff1a;需要使用的是谷歌浏览器&#xff0c;我的火狐浏览器试了不行&#xff0c;都没选项&#xff0c;火狐的出来的界面是这样的&#xff1a; 目录 具体操作如下&#xff1a; 1、谷歌…...

下一代存储解决方案:湖仓一体

文章首发地址 湖仓一体是将数据湖和数据仓库相结合的一种数据架构&#xff0c;它可以同时满足大数据存储和传统数据仓库的需求。具体来说&#xff0c;湖仓一体可以实现以下几个方面的功能&#xff1a; 数据集成&#xff1a; 湖仓一体可以集成多个数据源&#xff0c;包括结构…...

IntelliJ IDEA 2023.2.1 修复版本日志

我们刚刚发布了 v2023.2 的第一个错误修复更新。 您可以从 IDE 内部、使用工具箱应用程序或通过快照&#xff08;如果您使用的是 Ubuntu&#xff09;更新到此版本。您也可以直接从我们的网站下载。 以下是最新版本中包含的最值得注意的改进和修复的列表&#xff1a; 我们已经解…...

算法通关村十三关 | 数组字符串加法专题

1. 数组实现整数加法 题目&#xff1a;LeetCode66&#xff0c;66. 加一 - 力扣&#xff08;LeetCode&#xff09; 思路 我们只需要从头到尾依次运算&#xff0c;用常量标记是否进位&#xff0c;需要考虑的特殊情况是digits [9,9,9]的时候进位&#xff0c;我们组要创建长度加1…...

k8s--基本概念理解

必填字段 在要创建的 Kubernetes 对象的文件中.yaml&#xff0c;您需要设置以下字段的值&#xff1a; apiVersion- 您使用哪个版本的 Kubernetes API 创建此对象 kind- 你想创建什么样的对象 metadata- 有助于唯一标识对象的数据&#xff0c;包括name字符串、UID和可选namesp…...

流媒体开发千问【持续更新】

H.264中IDR帧和I帧区别 H.264/AVC编码标准中&#xff0c;IDR帧和I帧都是关键帧&#xff0c;即它们都不依赖于其他帧进行解码。但是&#xff0c;它们之间存在明确的区别&#xff1a; 定义与功能&#xff1a; I帧&#xff08;Intra-frame&#xff09;&#xff1a;I帧是一个内部编…...

全球各国官方语言大盘点,英语不得不学哇。。。

因国家和地区范围界定不同&#xff0c;官方语言只是个相对概念。具体而言是一个国家通用的正式语言或认定的正式语言。它是为适应管理国家事务的需要&#xff0c;在国家机关、正式文件、法律裁决及国际交往等官方场合中规定一种或几种语言为有效语言的现象。官方语言也是一个国…...

【mq】如何保证消息可靠性

文章目录 mq由哪几部分组成rocketmqkafka 为什么需要这几部分nameserver/zookeeper可靠性 broker可靠性 生产者消费者 mq由哪几部分组成 rocketmq kafka 这里先不讨论Kafka Raft模式 比较一下&#xff0c;kafka的结构和rocketmq的机构基本上一样&#xff0c;都需要一个注册…...

疲劳检测-闭眼检测(详细代码教程)

简介 瞌睡经常发生在汽车行驶的过程中&#xff0c;该行为害人害己&#xff0c;如果有一套能识别瞌睡的系统&#xff0c;那么无疑该系统意义重大&#xff01; 实现步骤 思路&#xff1a;疲劳驾驶的司机大部分都有打瞌睡的情形&#xff0c;所以我们根据驾驶员眼睛闭合的频率和…...

大数据日常运维命令

1、HDFS NameNode /usr/local/fqlhadoop/hadoop/sbin/hadoop-daemon.sh start namenode /usr/local/fqlhadoop/hadoop/sbin/hadoop-daemon.sh stop namenode bin/hdfs haadmin -DFSHAAdmin -getServiceState n1 2、HDFS DataNode /usr/local/fqlhadoop/hadoop/sbin/hadoop-…...

解锁安全高效办公——私有化部署的WorkPlus即时通讯软件

在当今信息时代&#xff0c;高效的沟通与协作对于企业的成功至关重要。然而&#xff0c;随着信息技术的发展&#xff0c;保护敏感信息和数据安全也变得越来越重要。为了满足企业对于安全沟通和高效办公的需求&#xff0c;我们隆重推出私有化部署的WorkPlus即时通讯软件&#xf…...

IDEA使用git

文章目录 给所有文件配置git初始化本地仓库创建.gitignore文件添加远程仓库分支操作 给所有文件配置git 初始化本地仓库 创建.gitignore文件 添加远程仓库 分支操作 新建分支 newbranch 切换分支 checkout 推送分支 push 合并分支 merge...

【跟小嘉学 Rust 编程】十八、模式匹配(Patterns and Matching)

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…...

keepalived+lvs+nginx高并发集群

keepalivedlvsnginx高并发集群 简介&#xff1a; keepalivedlvsnginx高并发集群&#xff0c;是通过LVS将请求流量均匀分发给nginx集群&#xff0c;而当单机nginx出现状态异常或宕机时&#xff0c;keepalived会主动切换并将不健康nginx下线&#xff0c;维持集群稳定高可用 1.L…...

剑指Offer65.不用加减乘除做加法 C++

1、题目描述 写一个函数&#xff0c;求两个整数之和&#xff0c;要求在函数体内不得使用 “”、“-”、“*”、“/” 四则运算符号。 示例: 输入: a 1, b 1 输出: 2 2、VS2019上运行 使用位运算的方法 #include <iostream>class Solution { public:/*** 计算两个整…...

【linux命令讲解大全】004.探索Linux命令行中的chmod和chown工具

文章目录 chmod概要主要用途参数选项返回值例子 chown补充说明语法选项参数实例 从零学 python chmod 用来变更文件或目录的权限 概要 chmod [OPTION]... MODE[,MODE]... FILE... chmod [OPTION]... OCTAL-MODE FILE... chmod [OPTION]... --referenceRFILE FILE...主要用途…...

nginx会话保持

ip_hash:通过IP保持会话 作用&#xff1a; nginx通过后端服务器地址将请求定向的转发到服务器上。 将客户端的IP地址通过哈希算法加密成一个数值 如果后端有多个服务器&#xff0c;第一次请求到服务器A&#xff0c; 并在务器登录成功&#xff0c;那么再登录B服务器就要重新…...

SpringBoot使用Druid连接池 + 配置监控页面(自定义版 + starter版)

目录 1. Druid连接池的功能2. 自定义版2.1 pom.xml添加依赖2.2 MyDataSourceConfig实现2.3 application.properties配置编写Controller进行测试2.4 druid监控页面查看 3. starter版3.1 pom.xml添加依赖3.2 自动配置分析3.3 使用application.properties对druid进行配置3.4 druid…...

【业务功能篇77】微服务-OSS对象存储-上传下载图片

3. 图片管理 文件存储的几种方式 单体架构可以直接把图片存储在服务器中 但是在分布式环境下面直接存储在WEB服务器中的方式就不可取了&#xff0c;这时我们需要搭建独立的文件存储服务器。 3.1 开通阿里云服务 针对本系统中的相关的文件&#xff0c;图片&#xff0c;文本等…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...

篇章二 论坛系统——系统设计

目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...

Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解

文章目录 一、开启慢查询日志&#xff0c;定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...

OpenGL-什么是软OpenGL/软渲染/软光栅?

‌软OpenGL&#xff08;Software OpenGL&#xff09;‌或者软渲染指完全通过CPU模拟实现的OpenGL渲染方式&#xff08;包括几何处理、光栅化、着色等&#xff09;&#xff0c;不依赖GPU硬件加速。这种模式通常性能较低&#xff0c;但兼容性极强&#xff0c;常用于不支持硬件加速…...

标注工具核心架构分析——主窗口的图像显示

&#x1f3d7;️ 标注工具核心架构分析 &#x1f4cb; 系统概述 主要有两个核心类&#xff0c;采用经典的 Scene-View 架构模式&#xff1a; &#x1f3af; 核心类结构 1. AnnotationScene (QGraphicsScene子类) 主要负责标注场景的管理和交互 &#x1f527; 关键函数&…...

AIGC 基础篇 Python基础 02

1.bool类型 书接上回&#xff0c;我们上次最后讲了三大数据类型&#xff0c;除了这三个之外&#xff0c;Python也有bool类型&#xff0c;也就是True和False。 a 2 print(a1) print(a2) 像这里&#xff0c;输出的内容第一个是False&#xff0c;因为a的值为2&#xff0c;而第…...

fast-reid部署

配置设置&#xff1a; 官方库链接&#xff1a; https://github.com/JDAI-CV/fast-reid# git clone https://github.com/JDAI-CV/fast-reid.git 安装依赖&#xff1a; pip install -r docs/requirements.txt 编译&#xff1a;切换到fastreid/evaluation/rank_cylib目录下&a…...