【C++】STL空间配置器

STL空间配置器

  • 一、什么是空间配置器
  • 二、为什么需要空间配置器
  • 三、SGI-STL空间配置器实现原理
    • 1、 一级空间配置器
    • 2、二级空间配置器
  • 四、优缺点分析

一、什么是空间配置器

STL 有六大组件分别是:容器,算法,迭代器, 空间配置器,适配器,仿函数

在我们日常使用STL中的容器时,我们是几乎感受不到空间配置器的存在,因为他一直在默默工作,整个STL的操作对象都存放在容器之中,而容器需要配置内存空间的,这个内存空间的配置就是空间配置器的作用:为各个容器进行高效的内存管理(内存的申请与回收)的

在这里插入图片描述

二、为什么需要空间配置器

对于STL中的容器在申请内存时当然可以选择newmalloc 来进行内存的申请,但是有以下不足之处:

  • 空间申请与释放需要用户自己管理,容易造成内存泄漏
  • 频繁向系统申请小块内存块,容易造成内存碎片,而且影响程序运行效率
  • 直接使用mallocnew进行申请,每块空间前有额外空间浪费
  • 代码结构比较混乱,代码复用率不高

因此STL需要设计一块高效的内存管理机制,于是就有了空间配置器,空间配置器是专门针对STL设计的一个小型内存池。

在这里插入图片描述

三、SGI-STL空间配置器实现原理

上面提到的几点不足之处,最主要还是:频繁向系统申请小块内存造成的。那什么才算是小块内存?SGI版本的STL以128作为小块内存与大块内存的分界线,将空间配置器其分为两级结构:

  • 一级空间配置器处理大块内存。
  • 二级空间配置器处理小块内存。

下面是空间配置器的宏观结构,我们在后面会一点一点丰富该结构:
在这里插入图片描述

1、 一级空间配置器

一级空间配置器原理非常简单,直接对maloc,free,realloc等C函数进行封装,并增加了C++中set_new_handle思想,形成了allocate、deallocate、reallocate 等函数用于来用于内存分配。

  • set_new_handler 是 C++ 中的一种机制,用于在内存分配失败时指定一个处理函数。这个处理函数可以尝试释放一些资源,以便内存分配操作能够成功,或者采取其他适当的措施。
  • 当使用 new 运算符分配内存时,如果内存分配失败,默认情况下会抛出 std::bad_alloc 异常。然而,通过 std::set_new_handler 函数,你可以指定一个自定义的处理函数,当内存分配失败时,这个处理函数会被调用。

一级空间配置器中大致过程是:

  1. 直接allocate分配内存,其实就是malloc来分配内存,成功则直接返回,失败就调用处理函数。
  2. 如果用户自定义了内存分配失败的处理函数就调用,没有的话就抛出一个异常
  3. 如果自定义了处理函数就进行处理,处理以后再继续尝试分配内存。

在这里插入图片描述

以下是一级空间配置器的核心部分的源码:

在这里插入图片描述

2、二级空间配置器

二级空间配置器专门负责处理小于128字节的小块内存,如何才能提升小块内存的申请与释放的方式呢?

  1. SGI-STL采用了内存池的技术来提高申请空间的速度以及减少额外空间的浪费。
  2. 采用哈希桶的方式来管理从内存池中申请的空间,来提高用户获取空间的速度与高效管理。
  • 内存池
    内存池就是:先申请一块比较大的内存块已做备用,当需要内存时,直接到内存池中去取,当池中空间不够时,再向内存中去取,当用户不用时,直接还回内存池即可。避免了频繁向系统申请小块内存所造成的效率低、内存碎片以及额外浪费的问题。
    在这里插入图片描述
  • SGI-STL中二级空间配置器设计
    二级空间配置器的设计是采用了哈希桶的方式来管理从内存池中得到的内存,其中桶的哈希值都是8的整数倍,SGI-STL将用户申请的内存块向上对齐到了8的整数倍。

问题: 内存块为什么必须要以8位单位呢,为什么不把1作为单位?

因为用户申请的空间基本都是4的整数倍,其他大小的空间几乎很少用到,还有就是由于了空间配置器中的内存块是像链表一样连接起来的,这样就必然需要指针来维护, 32位平台下指针4字节,64位平台下指针8字节,所以内存块最小的节点也要能存放一个指针。


在这里插入图片描述

  1. 二级空间配置器内部维护16条自由链表,分别是0-15号链表,最小8字节,以8字节逐渐递增,最大128字节,当你传入一个字节参数,表示你需要多大的内存时,在二级空间配置器内部会自动帮你对齐到第几号链表(如需要12bytes空间,二级空间配置器会分配16bytes大小)

  2. 在找到对应的哈希桶以后,二级空间配置器会先查看链表是否为空,如果不为空,直接从对应的free_list中取出内存节点,然后哈希桶的指针指向下一个节点。

  3. 如果哈希桶对应的free_list为空,先看其内存池是不是空时,如果内存池不为空:

    • 先检验它剩余空间是否够20个节点大小( 即所需内存大小提升后的大小 × 20 即所需内存大小提升后的大小 \times 20 即所需内存大小提升后的大小×20),若足够则直接从内存池中拿出20个节点大小空间,将其中一个分配给用户使用,另外19个当作自由链表中的区块挂在相应的free_list下,这样下次再有相同大小的内存需求时,可直接取出。
    • 如果不够20个节点大小,则看它是否能满足1个节点大小,如果够的话则直接拿出一个分配给用户,然后从剩余的空间中分配尽可能多的节点挂在相应的free_list中。
    • 如果连一个节点内存都不能满足的话,则先将内存池中剩余的空间挂在相应的free_list中,然后再给内存池申请内存。
  4. 内存池为空时,此时二级空间配置器会使用maloc从堆上申请内存,(一次所申请的内存大小为: 2 ∗ 提升后所需节点内存大小 ∗ 20 + 一段额外空间 2 * 提升后所需节点内存大小*20 + 一段额外空间 2提升后所需节点内存大小20+一段额外空间),一共申请40块,一半拿来用,一半放内存池中等待备用。

  5. 如果二级空间配置器malloc没有成功,说明heap上没有足够空间分配给我们了,此时,二级空间配置器会从比所需节点空间大的free_list中一 一搜索,从比它所需节点空间大的free_list中拔除一个节点来使用,如果这也没找到,说明比其大的free_list中都没有自由区块了,那就要调用一级空间配置器了(因为一级空间配置器内部可能设置的有内存分配失败的处理函数,通过该处理函数可能能够得到内存),如果第一级配置器的 malloc() 也失败了,就发出 bad_alloc 异常。

在这里插入图片描述

  • 释放时调用deallocate函数,若释放的n>128,则调用一级空间配置器,否则就直接将内存块挂上自由链表的合适位置。

四、优缺点分析

STL二级空间配置器虽然解决了外部碎片与提高了效率,但它同时增加了一些缺点:

  1. 因为自由链表的管理问题,它会把我们需求的内存块自动提升为8的倍数,这时若你需要1个字节,它会给你8个字节,即浪费了7个字节,所以它又引入了内部碎片的问题,若相似情况出现很多次,就会造成很多内部碎片,降低了内存的利用率。
  2. 二级空间配置器是在堆上申请大块的狭义内存池,然后用自由链表管理,供STL容器进行使用,在程序执行过程中,它将申请的内存一块一块都挂在自由链表上,即不会还给操作系统,并目它的实现中所有成员全是静态的(为了让空间配置器在整个进程中自由一个实例),所以它申请的所有内存只有在进程结束才会释放内存,还给操作系统,由此带来的问题有:
    • 如果不断的开辟小块内存,最后会导致整个堆上的空间都被挂在自由链表上,此时如果想开辟大块内存就会失败。
    • 若自由链表上持很多内存块没有被使用,当前进程的空间配置器又占着内存不能释放,这时其它的进程申请不到空间,也不可以使用当前进程的空闲内存,由此就会引发多种问题。

在gcc 4.9之后对于STL空间配置器就没有一级了,只有第二级。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

创建第一个Springboot项目HelloWorld

目录 一、准备工作 一、创建springboot项目 三、使用git上传到代码仓库gitee 四、git使用过程问题总结 一、准备工作 安装jdk:8u201(可以使用高一点的版本) jdk所有版本下载:Java Archive | Oracle 安装maven:不用…

“改进型”Howland 电流泵电路

“改进型”Howland 电流泵电路 “改进型”Howland 电流泵是一种使用差分放大器在分流电阻器 (Rs) 上施加电压的电路,从而产生能够驱动大范 围负载电阻的双极性(拉电流或灌电流)压控电流源。 设计注释 确保运算放大器的输入端(V…

Vue19-key的原理

一、v-for中key的作用 给节点进行一个标识&#xff0c;类似于身份证号。 1-1、需求1&#xff1a; 点击按钮&#xff0c;在<li>的最前面添加一个老刘的信息 <body><div id"root"><h1>人员信息</h1><button click.once"add&qu…

深度学习-注意力机制和分数

深度学习-注意力机制 注意力机制定义与起源原理与特点分类应用领域实现方式优点注意力机制的变体总结注意力分数定义计算方式注意力分数的作用注意力分数的设计总结 注意力机制&#xff08;Attention Mechanism&#xff09;是一个源自对人类视觉研究的概念&#xff0c;现已广泛…

NEFU服务科学与SOA

一、现代服务业与SSME 现代服务业 传统服务业 新业务模式 新型IT技术 知识密集 IT服务&#xff1a;由专门的IT组织向企业用户所提供的业务过程与功能性服务&#xff0c;以支持企业用户业务的正常运转。 现代服务业的四大领域 &#xff1a; 基础服务 生产服务 生活服…

怎么使用手机远程访问电脑文件?(3种方法)

手机远程访问电脑文件 “有时&#xff0c;当我离开电脑时&#xff0c;仍然需要访问和使用桌面上的文件。是否有一种工具可以通过WiFi而不是USB连接&#xff0c;让我的手机远程访问电脑上的文件&#xff1f;如果有任何建议&#xff0c;我将非常感激&#xff01;” 除了希望手机…

高效换热管

绕管式高效换热器 绕管换热器是一种结构紧凑&#xff0c;传热效率高的新型高效换热器。换热管按螺旋线形状交替缠绕在芯筒与外筒之间&#xff0c;相邻两层螺旋状换热管旋向相反&#xff0c;并采用一定形状的定距元件使之保持一定间距。层与层间换热管反向缠绕&#xff0c;极大…

800W-2300W-4500W-7000W线绕电阻器的选型参考

EAK线绕电阻器将普通电阻器材料的高脉冲稳定性与优化的导热和高度保护相结合。安装在导热表面上可进一步改善散热并提高稳定性。 EAK提供各种外壳设计和材料&#xff08;如铝和钢&#xff09;的导线电阻器。它们符合 UL508 的要求&#xff0c;在用作制动、充电、放电或加热电阻…

笨蛋学算法之LeetCodeHot100_3_最长连续序列(Java)

package com.lsy.leetcodehot100;import java.util.Arrays; import java.util.HashSet; import java.util.Set;public class _Hot3_最长连续序列 {public int longestConsecutive(int[] nums) {//创建set去重//对重复的数字进行去重Set<Integer> set new HashSet<>…

什么是校园抄表系统?

1.校园抄表系统的简述 校园抄表系统是当代高校管理中的一个重要组成部分&#xff0c;主要运用于全自动搜集、管理方法与分析校园里的电力能源使用数据&#xff0c;如水电煤等。它通过先进的方式方法&#xff0c;完成了对能源消耗的实时监控系统&#xff0c;提升了电力能源管理…

redis设计与实现(四)服务器中的数据库

服务器中的数据库 Redis服务器将所有数据库都保存在服务器状态server.h结构的db数组中&#xff0c;db数组的每个项都是一个redis.h/redisDb结构&#xff0c;每个redisDb结构代表一个数据库。 在初始化服务器时&#xff0c;程序会根据服务器状态的dbnum属性来决定应该创建多少…

CSS从入门到精通——背景样式

目录 背景颜色 任务描述 相关知识 背景色 编程要求 背景图片 任务描述 相关知识 背景图片 设置背景图片 平铺背景图像 任务要求 背景定位与背景关联 任务描述 相关知识 背景定位 背景关联 简写背景 编程要求 背景颜色 任务描述 本关任务&#xff1a;在本关…

PHP框架详解- symfony框架

GPT-4 (OpenAI) Symfony 是一个用 PHP 语言编写的开放源代码的 web 应用框架。Symfony 提供了一组可重用的组件和一个标准化、可扩展的框架&#xff0c;用于构建 web 应用、API、微服务等。它跟其他流行 PHP 框架&#xff08;比如 Laravel&#xff09;一样&#xff0c;旨在加快…

MySQL查询ab字段相同取时间最大的一条数据

MySQL是一个开源的关系型数据库管理系统&#xff0c;被广泛用于各种Web应用程序和大型企业级数据库系统。在实际应用中&#xff0c;经常会遇到需要查询某个字段相同的多条数据中&#xff0c;取时间最大的一条数据的需求。本文将通过代码示例来详细介绍如何使用MySQL实现这一功能…

内网Docker镜像无法使用?Debian/Ubuntu离线安装Dokcer

离线安装Docker Centos7停止技术支持&#xff0c;Dockerhub国内镜像也用不了&#xff0c;该教程只解决debian/ubuntu如何离线安装docker 卸载冲突的包 for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done先…

Kafka生产者消息发送流程原理及源码分析

Kafka是一个分布式流处理平台,它能够以极高的吞吐量处理数据。在Kafka中,生产者负责将消息发送到Kafka集群,而消费者则负责从Kafka集群中读取消息。本文将探讨Kafka生产者消息发送流程的细节,包括消息的序列化、分区分配、记录提交等关键步骤。 先看一个生产者发送消息的代…

【QT】记录一次QT程序发布exe过程

记录一次QT程序发布exe过程 使用windeploy与enigma发布独立的QT程序第一步 QT编译输出 **release** 版本第二步 QT 自带 windepoyqt 补全链接库第三步 enigma virtual box压缩打包为单一exe最后【2024-06-07 17】- 【补充】 贴一个自己用的bat脚本【**QtDeploy2exe.bat**】半自…

python数据分析--- ch3-5 python数字类型、算术运算符及流程控制语句

python数据分析--- ch3-5 python数字类型、算术运算符及流程控制语句 1.Ch3--数字类型的数据1.1 Python中的数据类型1.1.1整数类型(int)1.1.2 浮点类型(float)1.1.3复数类型(complex)1.1.4 布尔类型(bool) 1.2 数字类型的相互转换1.2.1 隐式类型的转换1.2.2 显式类型的转换 2. …

Python编程基础5

邮件编程 SMTP&#xff08;Simple Mail Transfer Protocol&#xff09;简单邮件传输协议&#xff0c;使用TCP协议25端口&#xff0c;它是一组用于由源地址到目的地址传送邮件的规则&#xff0c;由它来控制信件的中转方式。python的smtplib提供了一种很方便的途径发送电子邮件。…

惠州惠城:可燃气体报警器定期校准检测,安全更放心

在惠州惠城这片繁华的土地上&#xff0c;工业发展日新月异&#xff0c;安全问题愈发受到重视。其中&#xff0c;可燃气体报警器作为预防火灾和爆炸事故的重要设备&#xff0c;正在越来越多的场所得到应用。 今天&#xff0c;佰德就来探讨一下可燃气体报警器在惠州惠城的重要性…