图解SGI_STL空间配置器原理

2023-12-31 02:40
文章标签 配置 原理 图解 空间 stl sgi

本文主要是介绍图解SGI_STL空间配置器原理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简介

STL(Standard Template Library,标准模板库),实现版本不止一种,例如HP版本、P.J版本、RW版本。

本文主要剖析的是SGI版本的SGL,实现版本详情以及简介此处不过多赘述,可另行搜索了解

STL六大组件相互关系如图

在这里插入图片描述

六大容器关系如图中描述,此处对仿函数可以协助算法完成不同的策略的变化,适配器可以修饰仿函数做出例举说明

struct Person
{std::string m_name;int m_age;
};
bool comparePerson(const Person& left, const Person& right)
{return left.m_name == right.m_name && left.m_age == right.m_age;
}
bool comparePersonName(const Person& left, const std::string& right)
{return left.m_name == right;
}
bool comparePersonAge(const Person& left, const int& right)
{return left.m_age == right;
}

在这里插入图片描述

std::bind即通用函数适配器,通过适配器指定算法不同的操作方式即可得到不同的结果,即上文提及的仿函数可以协助算法完成不同的策略的变化,如std::find_if绑定的是comparePerson,则查找的必须是人员名称和年龄都需要满足的数据,绑定comparePersonName,则只需要人名相同的数据,绑定comparePersonAge,则查找的是年龄相同的数据。绑定函数不同,即策略的差异性,算法操作会得出不同的结果

SGI空间配置器

接下来就是本文的编写的目的,详细说明SGI空间配置器

1、为什么需要一个空间配置器管理内存(后文若提及内存,和空间配置器是同一概念,空间配置器有点拗口)

直接使用 ::operator new() 和 ::operator delete() 或者 malloc()和free()申请和释放内存会存在以下几个问题:

1.1、小块内存频繁申请释放带来性能问题

申请内存需要和系统交互,系统需要寻找空闲可用的空间,没有合适大小的空间会进行可用空闲空间的碎片的合并等等一系列的操作,这些操作是一种性能上的消耗。

1.2、小块内存带来的内存碎片化问题

过多小块内存申请会创造外部碎片,同时也会导致一页内存中存在内部碎片

1.3、小区块配置时的额外负担

向系统申请到的内存,系统还需要额外的空间来管理内存

2、SGI空间配置器的特殊配置能力-双层级配置能力

在这里插入图片描述

所谓双层配置能力指的就是较大片的内存(>128字节)使用一级配置器,即直接使用malloc()和free()申请和释放,对于 <=128字节的内存块使用二级配置器,二级空间配置器会预申请大块内存并分割为小块内存,使用链表的方式管理分割后的小块内存。

2.1第一层空间配置器

在这里插入图片描述

_S_oom_malloc(__n)是注册的异常处理函数,例如内存空间不足,申请内存失败的情况下,会执行_S_oom_malloc实现的操作,默认是直接返回异常。

2.2、第二层空间配置器

在这里插入图片描述

3、空间配置器分配管理细节剖析(图中列举的函数是SGI源码中的函数, stl_alloc.h)

内存申请流程图如下所示,红色箭头为申请函数入口处执行起始位置,根据源码的条件判断以及处理方案整理出以下流程图,具体细节可以结合后文具体示例的图解和文字描述进行整理流程的梳理。
在这里插入图片描述

(1)allocate(size_t __n)函数中首先判断当前申请申请的内存大小(n),当(n>128)时使用一级空间配置器,执行流程 2.1小结图中已展示。

(2)申请(<=128)字节的内存块,即使用的是二级空间配置器
在这里插入图片描述
上图为下文图解中各个颜色区域代表的含义简述。

示例1:当前需要申请20字节空间,按照8的倍数向上对齐后即需要获取的内存块大小为24字节,管理24字节大小内存的链表由数组第3个元素记录,以下为示例的主要步骤:

在这里插入图片描述

第一步,申请的内存块内存大小对齐后,需要的是24字节,对应数组元素的第三位,此时存储的值为0x0000,表示当前管理的链表为空,即没有可直接取出使用的内存块,此时_S_start_free==_S_end_free说明当前内存池中也没有可以用来分配使用的内存,则需要重新申请大块内存。

第二步:申请内存块的代销缺省值 total_bytes = 24 * 20 = 480字节,_S_heap_size=0,所以此次实际申请一次大块内存的大小bytes_to_get=480字节(计算公式:bytes_to_get=2 * total_bytes + _S_round_up(_S_heap_size >> 4),图中假设当前通过一级配置器申请的内存起始地址为 0x1000,申请成功的情况下_S_heap_size=480(_S_heap_size+=__bytes_to_get)

第三步:将大块内存分割,共计分割20块大小为24字节的内存块,将第一块地址(0x1000)返回给用户,第2~20块挂载到存储24字节大小的链表管理数组中,此时第三个数组元素值变为第2个内存块的地址(0x1018),第2个内存块大小为24字节,存储的是第三个内存块的起始地址(0x1030),按照以上方式会将剩下的19个内存块组成一个链表结构,最后一个内存块的地址即为0x0000。

示例1总结:通过以上三步返回一块用户申请的内存,同时预申请了部分内存,并通过链表管理,下一次申请24字节内存块时,只需要从链表中取出一个内存块返回地址即可。

示例2:在示例1完成的基础上,再次申请一个24字节的内存块,以下为示例的主要步骤:

在这里插入图片描述

第一步:检测到管理24字节大小的内存块链表不为空,即可以直接从链表中取出一个内存块返回给用户,优先取出链表中的首个内存块。

第二步:更新链表的指向,需要将数组元素中记录的地址改为取出返回的内存块中的地址(链表结构头删的处理操作,即更改指针指向)

示例3:假设当前申请8字节空间,检测到8字节大小的内存块链表中没有管理可用的空闲内存块,但是当前内存池的中还存在足够缺省申请20个块的内存(内存池空闲空间 > 160字节)可用的空间(即_S_start_free != _S_end_free且 _S_end_free-S_start_free > 160字节),以下为示例的主要步骤:

在这里插入图片描述

第一步:图中当前管理8字节大小的链表为空,即没有可用内存块,而预申请的大块内存池中存在的内存大小为168字节,是可以用于分割为20块8字节的内存块,(注意:此处不要因为曲解示例1,认为每次申请的大块内存刚好是所需字节的20倍,计算公式:bytes_to_get=2 * total_bytes + S_round_up(S_heap_size >> 4中会根据S_heap_size的值得出大块内存申请的预判扩增值,示例1讲述的是初次申请,S_heap_size默认值为0,随着内存的申请,S_heap_size会加上当前申请的大块内存的大小,所以示例1申请完内存后S_heap_size的值为480,后续再次申请会在480基础上增加,因此会出现本例中分配之前以前存在足够的大块内存)。

第二步:如同示例1进行内存块分割,将第一块内存的地址返回给用户,在8字节大小管理链表中追加19个8字节大小内存块进行管理,并更改内存池的起始地址_S_start_free=0x2000,当前内存池中余下8字节的空间。

示例3:假设当前申请120字节空间,检测到管理120字节大小的内存块链表中没有管理可用的空闲内存块(即链表为空),但是当前内存池的中还存在248字节,不够缺省申请的20个内存块总和(20*120字节=2400字节),但是满足至少一个内存块的申请(内存池空闲空间 < 2400字节)可用的空间

在这里插入图片描述

第一步:判断120字节大小的管理链表为空,此时内存池剩余可用内存的大小是248字节,按照默认缺省申请20个内存块,实际需要2400字节,当前剩余内存池内存不够分配,但是可以至少分配一个内存块,因此不需要重新申请内存,可以直接对剩余内存池中内存进行分割。

第二步:首先将内存中的前120字节的内存块(地址:0x3000)地址返回给用户使用,还可用分配出120字节内存块(地址:0x3078)挂载到120字节大小的链表中,此时还余下8字节内存块,不满足120字节的分配,余下的8字节会挂载到管理8字节大小内存块的链表中。(注意:8字节的链表中存在可用内存块,此时新增的内存块会插入链表的首位,内存记录的值更新为原首位内存块的地址)。

示例4:申请8字节大小的内存块,此时管理8字节大小的内存块链表为空,allocate内存也失败的情况下(可以认为系统没有空闲内存提供了),会尝试依次向后查找,较大字节管理的链表是否有挂接的内存块,有则将其还回内存池,再进行分配

在这里插入图片描述

第一步:8字节大小内存块链表管理为空,内存池为空,此时通过一级配置器申请160字节失败。依次检查管理16,24,32 … 128字节大小的链表中是否有可用内存块,图中所示最先在管理24字节内存块的链表中检测到可用内存块。

第二步:取出24字节大小内存块管理链表中的第一块内存,此时更新_S_start_free=0x4000,_S_end_free=0x4018,即达到取出一块内存返回内存池的目的。

第三步:将内存池中的内存取出8字节返回给用户,余下的挂载到管理8字节大小内存块的链表中。

4、内存释放

内存释放,如果是大于128字节的内存会代用一级配置中释放接口,实际就是直接free了。如果释放的内存小于128字节,会将其挂接点对应大小块的链表中,假设当前释放一块内存首地址为(0x4000)大小为24字节的内存块,流程如下图所示,会将内存块挂载到对应内存管理链表中
在这里插入图片描述

5、总结

上文的示例就是将流程图中的流程具体化的列举分析,可以理解示例后再看下流程图,流程图中的函数名称都是来自源码。
流程如下图所示,会将内存块挂载到对应内存管理链表中

[外链图片转存中…(img-vD9DiqNp-1687711050549)]

5、总结

上文的示例就是将流程图中的流程具体化的列举分析,可以理解示例后再看下流程图,流程图中的函数名称都是来自源码。

这篇关于图解SGI_STL空间配置器原理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/554735

相关文章

mybatis映射器配置小结

《mybatis映射器配置小结》本文详解MyBatis映射器配置,重点讲解字段映射的三种解决方案(别名、自动驼峰映射、resultMap),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定... 目录select中字段的映射问题使用SQL语句中的别名功能使用mapUnderscoreToCame

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

MySQL8 密码强度评估与配置详解

《MySQL8密码强度评估与配置详解》MySQL8默认启用密码强度插件,实施MEDIUM策略(长度8、含数字/字母/特殊字符),支持动态调整与配置文件设置,推荐使用STRONG策略并定期更新密码以提... 目录一、mysql 8 密码强度评估机制1.核心插件:validate_password2.密码策略级

ShardingProxy读写分离之原理、配置与实践过程

《ShardingProxy读写分离之原理、配置与实践过程》ShardingProxy是ApacheShardingSphere的数据库中间件,通过三层架构实现读写分离,解决高并发场景下数据库性能瓶... 目录一、ShardingProxy技术定位与读写分离核心价值1.1 技术定位1.2 读写分离核心价值二

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

Three.js构建一个 3D 商品展示空间完整实战项目

《Three.js构建一个3D商品展示空间完整实战项目》Three.js是一个强大的JavaScript库,专用于在Web浏览器中创建3D图形,:本文主要介绍Three.js构建一个3D商品展... 目录引言项目核心技术1. 项目架构与资源组织2. 多模型切换、交互热点绑定3. 移动端适配与帧率优化4. 可

QT Creator配置Kit的实现示例

《QTCreator配置Kit的实现示例》本文主要介绍了使用Qt5.12.12与VS2022时,因MSVC编译器版本不匹配及WindowsSDK缺失导致配置错误的问题解决,感兴趣的可以了解一下... 目录0、背景:qt5.12.12+vs2022一、症状:二、原因:(可以跳过,直奔后面的解决方法)三、解决方