【C++】STL空间配置器

news2024/11/8 9:36:29

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空间配置器就没有一级了,只有第二级。

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

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

相关文章

【最新鸿蒙应用开发】——类Web开发范式2——前端语法

兼容JS的类Web开发范式 JS FA应用的JS模块(entry/src/main/js/module)的典型开发目录结构如下: 1. 项目基本结构 1.1. 目录结构 1.2. 项目文件分类如下: .hml结尾的HML模板文件,这个文件用来描述当前页面的文件布局结构。 .css结尾的CSS样…

Wall国内开源程序照片墙,支持VR全景及安装教程

下载 GitHub官网:https://github.com/zhang-tong-yao/wall 软件下载:https://github.com/zhang-tong-yao/wall/releases,推荐下载最新的版本。 演示效果 目前支持PC端和移动端自适应。 演示地址:https://demo-wall.ityao.cn …

芯片键合电阻器

引线键合电阻器(绑定电阻器)超稳定,可靠性高。激光修整至严格公差。可自定义的值和该值的唯一标记。该器件非常适合但不限于混合电路应用。与二极管芯片,MOS芯片,IGBT芯片键合封装。 芯片键合电阻器优势和特点 顶部触…

探索AOSP中的RRO:运行时资源覆盖的奥秘

探索AOSP中的RRO:运行时资源覆盖的奥秘 在Android开发中,为了提供更大的灵活性和可定制性,Android提供了一种关键特性:运行时资源覆盖(Runtime Resource Overlay,简称RRO)。本文将深入探讨RRO在Android开源项目(AOSP)中的作用及其实现方法。 什么是运行时资源覆盖(…

Vue19-key的原理

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

A股上市公司长短期并购绩效CAR、BHAR数据(2008-2022年)

数据简介&#xff1a; 短期并购绩效以首次公告日前后5个交易日内持有并购方股票的累计超额回报率[CAR(-5,5)]作为短期并购绩效的衡量指标。为计算并购方的累计超额回报率&#xff0c;定义首次公告日前的150个交易日至首次公告日前的30个交易日为估计窗口期&#xff0c;以窗口期…

今日成果2024-6-7 TrustZone TEE安全SDK开发指南

Rockchip Vendor Storage Application Note.pdf OK 开机下&#xff0c;可以实现Vendor Storage的读写。 0ms时同步RTC时间 OK Rockchip_Developer_Guide_TEE_SDK_CN.pdf 什么是TrustZone 此系统方法意味着可以保护安全内存、加密块、键盘和屏幕等外设&#xff0c;从而可确…

c++中string用法详解

目录 二、案例需求 三、案例实现 1.首先获取strData中的角色数量 2.创造结构体数组&#xff0c;定义两个索引值 3.循环遍历对结构体User中的Id和Exp进行赋值 4.对结构体数组userArr进行排序 5.展示结果以及最终代码 ​四、最后 一、前言 在C中&#xff0c;std::string …

可视化剪辑,账号矩阵管理,视频分发,聚合私信多功能一体化营销工具 源代码开发部署方案

可视化剪辑&#xff0c;账号矩阵管理&#xff0c;视频分发&#xff0c;聚合私信多功能一体化营销工具 源代码开发部署方案 可视化剪辑&#xff1a; 可视化剪辑开发是一种通过图形化界面和拖放操作&#xff0c;以可视化的方式进行影片剪辑和编辑的开发方法。它可以让非专业用户…

什么是校园抄表系统?

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

maven基本操作和配置(idea版基础版)

写在前面&#xff1a;为一位朋友写的一个博客&#xff0c;有需要都可以查看&#xff01; 一、maven是什么&#xff1f; 一句话&#xff1a;管理依赖工具&#xff0c;统一项目结构便于开发&#xff0c;把项目开发和管理的过程抽象成对象模型来管理&#xff08;pom模型&#xf…

@JsonValue和@JsonCreator使用

当实体类中的属性为枚举类型时&#xff0c;将其序列化成json字符串传给前端&#xff0c;传递的应该是有效的值而不是枚举常量。 1. Get-Started Data public class Student {private Long id;private String userName;private String telephone;private String email;private …

C语言 | Leetcode C语言题解之第145题二叉树的后序遍历

题目&#xff1a; 题解&#xff1a; void addPath(int *vec, int *vecSize, struct TreeNode *node) {int count 0;while (node ! NULL) {count;vec[(*vecSize)] node->val;node node->right;}for (int i (*vecSize) - count, j (*vecSize) - 1; i < j; i, --j)…

【C语言】12.C语言内存函数

文章目录 1.memcpy使用和模拟实现2.memmove使用和模拟实现3.memset函数的使用4.memcmp函数的使用 memcpy&#xff1a;内存拷贝 memmove&#xff1a;内存移动 memset&#xff1a;内存设置 memcmp&#xff1a;内存比较 1.memcpy使用和模拟实现 memcpy&#xff1a;内存拷贝 void…

【python】OpenCV—Background Estimation(15)

文章目录 中值滤波中值滤波得到图像背景移动侦测 学习来自 OpenCV基础&#xff08;14&#xff09;OpenCV在视频中的简单背景估计 中值滤波 中值滤波是一种非线性平滑技术&#xff0c;主要用于数字信号处理&#xff0c;特别是在图像处理中去除噪声。 一、定义与原理 定义&am…

如何使用Python在word文档中创建表格

如何使用Python在word文档中创建表格 介绍效果代码 介绍 本文将介绍如何使用Python库python-docx在Word文档中创建表格。 效果 插入表格前的word文档&#xff1a; 插入表格后的word文档&#xff1a; 代码 from docx import Document# 加载现有的Word文档 doc Document(…

利安科技上市首日股价大涨:2023营收净利润下滑,募资金额大幅缩水

《港湾商业观察》施子夫 6月7日&#xff0c;宁波利安科技股份有限公司&#xff08;以下简称&#xff0c;利安科技&#xff09;正式在深交所创业板挂牌上市&#xff0c;股票简称为利安科技&#xff0c;股票代码300784。 上市当天&#xff0c;利安科技股价大涨348.76%。 2022年…

高考志愿填报,如何识别兴趣和擅长?

一年一度的高考落下帷幕&#xff0c;是不是就意味着放松了&#xff1f;解放了&#xff1f; 高考志愿填报的重要性&#xff0c;依旧重要&#xff0c;考得好不如报的好。 考分高低固然是关键&#xff0c;而填报高考志愿&#xff0c;才是真正决定人生的一次选择&#xff0c;这一…

vue3 defineComponent + 渲染函数h + 全局注册​

defineComponent 是 Vue 3 中的一个函数&#xff0c;用于定义一个组件。它是 Vue 3 的组合式 API 的一部分&#xff0c;提供了一种更加灵活和组织化的方式来定义组件。在 Vue 2 中&#xff0c;我们通常使用一个对象来定义组件&#xff0c;而在 Vue 3 中&#xff0c;defineCompo…

Python深度学习基于Tensorflow(17)基于Transformer的图像处理实例VIT和Swin-T

文章目录 VIT 模型搭建Swin-T 模型搭建参考 这里使用 VIT 和 Swin-T 在数据集 cifar10 上进行训练 VIT 模型搭建 导入需要的外部库 import numpy as np import tensorflow as tf import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec这里我们接着使用 ci…