STL空间配置器入门

news2024/9/22 3:58:46

STL简介
  STL(Standard Template Library,标准模板库),从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合。

谈及组件,那么我们就首先来简单谈下STL六大组件,其相关的设计模式使用,以及各组件之间的协作关系。

六大组件简单介绍
1. 空间配置器:内存池实现小块内存分配,对应到设计模式–单例模式(工具类,提供服务,一个程序只需要一个空间配置器即可),享元模式(小块内存统一由内存池进行管理)

2.迭代器:迭代器模式,模板方法

3.容器:STL的核心之一,其他组件围绕容器进行工作:迭代器提供访问方式,空间配置器提供容器内存分配,算法对容器中数据进行处理,仿函数伪算法提供具体的策略,类型萃取  实现对自定义类型内部类型提取。保证算法覆盖性。其中涉及到的设计模式:组合模式(树形结构),门面模式(外部接口提供),适配器模式(stack,queue通过deque适配得  到),建造者模式(不同类型树的建立过程)。

4.类型萃取:基于范型编程的内部类型解析,通过typename获取。可以获取迭代器内部类型value_type,Poter,Reference等。

5.仿函数:一种类似于函数指针的可回调机制,用于算法中的决策处理。涉及:策略模式,模板方法。

6适配器:STL中的stack,queue通过双端队列deque适配实现,map,set通过RB-Tree适配实现。涉及适配器模式。

 

STL空间配置器产生的缘由:

  在软件开发,程序设计中,我们不免因为程序需求,使用很多的小块内存(基本类型以及小内存的自定义类型)。在程序中动态申请,释放。

这个过程过程并不是一定能够控制好的,于是乎,

问题1:就出现了内存碎片问题。(ps外碎片问题)

问题2:一直在因为小块内存而进行内存申请,调用malloc,系统调用产生性能问题。

注:内碎片:因为内存对齐/访问效率(CPU取址次数)而产生 如 用户需要3字节,实际得到4或者8字节的问题,其中的碎片是浪费掉的。

  外碎片:系统中内存总量足够,但是不连续,所以无法分配给用户使用而产生的浪费。下边简单图解

  这两个问题解释清楚之后,就来谈STL空间配置器的实现细节了

实现策略

  用户申请空间大于128?

  yes:调用一级空间配置器
  no:调用二级空间配置器

大致实现为:

  二级空间配置由内存池以及伙伴系统:自由链表组成

  一级空间配置器直接封装malloc,free进行处理,增加了C++中的set_handler机制(这里其实也就是个略显牵强的装饰/适配模式了),增加内存分配时客户端可选处理机制。

可配置性:

  客户端可以通过宏__USE_MALLOC进行自定义选择是否使用二级空间配置器。

一级空间配置器就主要封装malloc,添加handler机制了,这里就不罗嗦了,相信各位都是可以通过源码了解到的

 

Trace使用

  对于内存池的内部实现过程共还是比较复杂的,虽然代码量,函数比较简单。但是调用过程可能比较复杂。这时,如果我们选择debug调试,过程会相当的繁琐,需要仔细记录调用堆栈过程以及数据流向,逻辑变更等。对于楼主这种水货来说,估计完事就要苦了。

  所以,就使用Trace进行跟踪,打印数据流向,逻辑走向,文件,函数,方法,行位置。那么我们就能根据这个记录进行程序的排错以及调优了。

具体Trace简单如下

#pragma once

#define ___TRACE(...) fprintf(fout, "file[%s]line[%u]func[%s]::",__FILE__,__LINE__,__func__);\
fprintf(fout,__VA_ARGS__)

没错,就是这么简单,利用宏打印文件,行,函数位置,然后利用可变参数列表方式接收代码中具体位置的记录跟踪。

如下是代码摘取的Alloc中的跟中。

static void *Allocate(size_t n)
    {
        ___TRACE("__MallocAllocTemplate to get  n = %u\n",n);
        void *result = malloc(n);
        if (0 == result)
        {
            result = OomMalloc(n);
        }
        return result;
    }

1.仔细探究源码之后,你一定会发现一个问题,
  貌似二级空间配置器中的空间重头到尾都没看到他归还给系统。那么问题就是,内存池空间何时释放?

对于这个问题,在回头浏览一下源码及结构图,你就会发现

  大于128的内存,客户程序Deallocate之后会调free释放掉,归还给了系统。

  但是呢……………

  内存池中获取的空间,最终,假定用户都调用Dealloc释放调了,那么他们又在哪里呢?

    没有还给系统,没有在内存池,在自由链表中。

Got it:程序中不曾释放,只是在自由链表中,且配置器的所有方法,成员都是静态的,那么他们就是存放在静态区。释放时机就是程序结束。

2.如果需要释放,那么应该怎么处理呢?
  因为真正可以在程序运行中就归还系统的只有自由链表中的未使用值,但是他们并不一定是连续的(用户申请空间,释放空间顺序的不可控制性),所以想要在合适时间(eg一级配置器的handler中释放,或者设置各阀值,分配空间量到达时处理),就必须保证释放的空间要是连续的。保证连续的方案就是:跟踪分配释放过程,记录节点信心。释放时,仅释放连续的大块。

3.关于STL空间配置器的效率考究
  既然已经存在,而又被广泛使用,那么,整体的效率,以及和STL内部容器之间的使用配合还是没问题的。

我们考虑几种情况:
  a. 用户只需要无限的char类型空间,然而配置器中却对齐到8,于是乎,整个程序中就会有7/8的空间浪费。

  b.对于假定用户申请N次8空间,将系统资源耗到一定程度,然后全部释放了,自由链表中的空间都是连续的。却没有释放。

    但是:用户需要申请大于8的空间时,却依旧没有空间可用。

总之:这个问题就是,空间可能全部积攒在小块自由链表中,却没有用户可用的。这就尴尬了。

STL空间配置器主要分三个文件实现:
(1)<stl_construct.h> :这里定义了全局函数construct()和destroy(),负责对象的构造和析构。

(2)<stl_alloc.h>:文件中定义了一、二两级配置器,彼此合作,配置器名为alloc。

(3)<stl_uninitialized.h>:这里定义了一些全局函数,用来填充(fill)或复制(copy)大块内存数据,他们也都隶属于STL标准规范。

首先需要说明的是二级空间配置器是由一个内存池和自由链表配合实现的。

 

srartFree就相当于水位线的一种东西,它标志着内存池的大小。

自由链表中其实是一个大小为16的指针数组,间隔为8的倍数。各自管理大小分别为8,16,24,32,40,48,56,64,72,80,88,96,104, 112,120,128 字节的小额区块。在每个下标下挂着一个链表,把同样大小的内存块链接在一起。此处特别像哈希桶。
自由链表结构:
这个结构可以看做是从一个内存块中抠出4个字节大小来,当这个内存块空闲时,它存储了下个空闲块,当这个内存块交付给用户时,它存储的时用户的数据。因此,allocator中的空闲块链表可以表示成:
obj* free_list[16];

obj* 是4个字节那么大,但是大部分内存块大于4。我们想要做的只是将一块块内存链接起来,我们不用看到内存里所有的东西,所以我们可以只用强转为obj*就可以实现大内存块的链接。

二级空间配置器是为频繁分配小内存而生的一种算法。其实就是消除一级空间配置器的外碎片问题。

ChunkAlloc要做的就是去找操作系统要内存,依次性要20个,但是我们要考虑很多情况:

内存池里有足够20块大的内存
内存池里有小于20块大于等于1块的内存大小
内存池里1块内存那么大都没有
STL是这样做的: 如果有足够的内存,那么一次性就给20块,返回第一块给用户,其余的挂在自由链表上。
只有一块或者多块,返回一块给用户。
没有内存了,找操作系统要。
操作系统没有了,启用最后一根救命稻草,调用一级空间配置器,通过句柄函数释放内存,分配内存。

这个就是二级空间配置器的主要逻辑结构。

还有要说明的几点就是:
空间配置器里所有的成员都是静态的。是为了在外面通过作用域就可以调用,而不需要构造对象。
空间配置器可以使用于大部分的数据结构,如List,vector等。
对于自由链表的初始化时特别容易错的。

template<bool threads, int inst>
typename DefaultAllocTemplate<threads, inst>::obj* volatile  
        DefaultAllocTemplate<threads, inst>::FreeList[FREELISTSIZE] = { 0 };

注意到typename了吗。它就为了完成一个功能,到苏编译器DefaultAllocTemplate<threads, inst>是一个类型,不然会出现错误

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/840251.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【雕爷学编程】Arduino动手做(186)---WeMos ESP32开发板5

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

队伍发展有一定的规律 队伍不好带

队伍发展有一定的规律&#xff0c;队伍不好带 塔克曼是研究组织行为学的 1977年提出了团队发展模型 有一定的借鉴意义 趣讲大白话&#xff1a;社会变了&#xff0c;带队伍不好带了 【趣讲信息科技247期】 **************************** 塔克曼团队发展的五个阶段是&#xff1a;…

数据结构——双链表

我宁愿靠自己的力量&#xff0c;打开我的前途&#xff0c;而不愿求有力者垂青 文章目录 双线向链表各接口函数名或变量名 双向链表接口实现源码 快速索引【头文件及函数声明】 双向链表接口实现 双向链表的构造分析 双向链表的定义及初始化 双向链表的插入和删除 往期…

网络安全设备及部署

什么是等保定级&#xff1f; 之前了解了下等保定级&#xff0c;接下里做更加深入的探讨 文章目录 一、网路安全大事件1.1 震网病毒1.2 海康威视弱口令1.3 物联网Mirai病毒1.4 专网 黑天安 事件1.5 乌克兰停电1.6 委内瑞拉电网1.7 棱镜门事件1.8 熊猫烧香 二、法律法规解读三、安…

SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--具体功能实现【三】

文章目录 SSM--功能实现实现功能04-添加家居信息需求分析/图解思路分析代码实现注意事项和细节 实现功能05-显示家居信息需求分析/图解思路分析 代码实现 SSM–功能实现 实现功能04-添加家居信息 需求分析/图解 思路分析 完成后台代码从dao -> serivce -> controller ,…

C++学习笔记总结练习:迭代器

迭代器 1 基础 头文件 #include<iterator>迭代器范围 begin和end被容器使用了&#xff0c;可以用front和back作为游标。 左闭右开区间 [begin,end)使用迭代器进行遍历 遍历方法有三种&#xff1a;下标遍历、范围for遍历、迭代器遍历 container c; first c.begin(); l…

【项目 计网2】4.4网络模型 4.5协议 4.6网络通信的过程

文章目录 4.4网络模型OSI七层参考模型TCP/IP四层模型&#xff08;常用&#xff09;简介四层介绍 4.5协议简介常见协议UDP协议TCP协议IP协议以太网帧协议&#xff08;MAC地址封装&#xff09;ARP协议&#xff08;IP->MAC&#xff09; 4.6网络通信的过程封装分用 4.4网络模型 …

【Spring】使用注解存储Bean对象

目录 一、配置扫描路径&#xff08;使用注解的方式存对象的前提&#xff09; 二、使用类注解存储Bean对象 1、使用五大类注解存储Bean对象 2、为什么要这么多的类注解&#xff1f; 2.1、五大类注解之间的关系 3、获取Bean对象时的默认命名规则 三、使用方法注解来存储…

【Paper】2020_网络化多智能体系统的事件触发一致性研究_徐勇

徐勇. 网络化多智能体系统的事件触发一致性研究[D].浙江大学,2020.DOI:10.27461/d.cnki.gzjdx.2020.001385. 文章目录 5 已知 DoS 攻击策略下多智能体系统的事件触发安全一致性分析5.1 引言5.2 数学模型与问题描述5.3 控制器和事件触发条件的设计5.5 数值仿真程序 Main.m程序 M…

.locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复

引言&#xff1a; 当今社会&#xff0c;互联网的迅速发展为我们的工作和生活带来了便利&#xff0c;但同时也伴随着越来越多的网络威胁。勒索病毒如.locked勒索病毒便是其中的代表之一。.locked勒索病毒利用高级加密算法&#xff0c;将用户重要的数据文件锁定&#xff0c;要求…

IP路由基础+OSPF 基础

IP路由 RIB与FIB RIB&#xff1a;Routing Information Base&#xff0c;路由信息库 &#xff0c;路由器的控制平面 FIB&#xff1a;Forwarding Information Base&#xff0c;转发信息库&#xff0c;路由器的数据平面 路由信息库主要是记录直连路由以及协议宣告的路由信息&am…

Windows安装子系统Linux

Windows安装子系统(Linux ubuntu&#xff09; 安装条件步骤1.安装WSL命令2.设置Linux用户名和密码3.写个简单的.c程序看看4.如何互传文件 安装条件 Windows 10版本2004及更高的版本才能安装。 步骤 1.安装WSL命令 我们可以使用WSL来安装子系统 Linux ubuntu(默认是这个)。 …

思科2021笔试题

笔试时间&#xff1a;2020.09.07&#xff0c;19&#xff1a;00——21&#xff1a;00 岗位&#xff1a;嵌入式软件工程师 题型&#xff1a;数据结构4道&#xff0c;网络3道&#xff0c;操作系统3道&#xff0c;C4道&#xff0c;Java4道&#xff0c;python4道&#xff0c;数据库…

ORCA优化器浅析——CQueryContext对优化器的要求

从ORCA优化器浅析——重要主流程概述中可以知道进入真正优化器引擎执行流程之前需要对优化器提出要求&#xff0c;比如后面会提到的required columns、required sort orders等。而CQueryContext即是承载这些内容的类。首先CQueryContext类是通过PqcGenerate函数构造的&#xff…

深入学习JVM —— GC垃圾回收机制

前言 前面荔枝已经梳理了有关JVM的体系结构和类加载机制&#xff0c;也详细地介绍了JVM在类加载时的双亲委派模型&#xff0c;而在这篇文章中荔枝将会比较详细地梳理有关JVM学习的另一大重点——GC垃圾回收机制的相关知识&#xff0c;重点了解的比如对象可达性的判断、四种回收…

推荐一款老化测试软件 Monitor.Analog

1. 数据采集模块&#xff1a; 该模块负责与下位机设备通信&#xff0c;实时采集模拟量数据。支持多种通信协议&#xff0c;如Modbus、OPC等&#xff0c;以适应不同类型的设备。数据采集模块还需要具备异常数据处理功能&#xff0c;例如数据丢失、错误数据等。 2. 数据存储模块…

Linux命令200例:用Look一个进行文本搜索工具

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f3c6;本文已…

Python简单应用V

题目 通过编写函数实现下述各题。 输入一字符串&#xff0c;各个子串之间按空白字符隔开&#xff0c;分别显式其中最长、最短子串&#xff0c;以及最大、最小字符。 输入单个字符&#xff0c;判断并显示该字符是否为大写英文字母、小写英文字母、非英文文字字符、空格、数字或…

【Paper Reading】DETR:End-to-End Object Detection with Transformers

背景 Transformer已经在NLP领域大展拳脚&#xff0c;逐步替代了LSTM/GRU等相关的Recurrent Neural Networks&#xff0c;相比于传统的RNN&#xff0c;Transformer主要具有以下几点优势 可解决长时序依赖问题&#xff0c;因为Transformer在计算attention的时候是在全局维度进行…

CentOS7---部署Tomcat和安装Jpress

总览需求 1. 简述静态网页和动态网页的区别。 2. 简述 Webl.0 和 Web2.0 的区别。 3. 安装tomcat8&#xff0c;配置服务启动脚本&#xff0c;部署jpress应用。1、简述静态网页和动态网页的区别 静态网页&#xff1a; 请求响应信息&#xff0c;发给客户端进行处理&#xff0c…