【项目日记(二)】开胃菜--定长池的实现

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:项目日记-高并发内存池⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习C++
  🔝🔝
开发环境: Visual Studio 2022


在这里插入图片描述


项目日记

  • 1. 前言
  • 2. 前期基础知识铺垫
  • 3. 定长池基础框架
  • 4. 定长池New的具体实现
  • 5. 对代码的解释
  • 6. 定长池delete的具体实现
  • 7. 总结以及小tips

1. 前言

在真正开始实现并发内存池前
我们先做一个子项目-定长池.
掌握了定长池对后面学习并发内存池
有很大的帮助,并且定长池本身也是
并发内存池的一个组件!

本章重点:

本篇文章着重讲解定长池的模拟实现
,其中涉及到自由链表的链接问题,如何
彻底放弃malloc的问题以及如何处理
32位和64位下指针大小的区分的问题


2. 前期基础知识铺垫

我们还需要补充两个知识的内容:

  1. 如何彻底舍弃malloc?
  2. 如何将free掉的资源再次利用?

windows/Linux下如何直接申请内存:

Windows下的申请方式

Linux下的申请方式

由于博主使用的开发环境是Windows
所以我这里直接使用此函数了:

inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32
	void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
	// linux下使用brk mmap等
#endif
	if (ptr == nullptr)
		throw std::bad_alloc();
	return ptr;
}

如何重复利用已经释放掉的内存:

采用自由链表的方式将所有释放
掉的空间全部连接在一起

在这里插入图片描述

有一个问题:既然自由链表中链接的是
已经释放掉的内存,那么就代表这块内
存已经没用了,所以在32/64位机器下,
我们直接让这份空间的头4/8个字节指
向下一份释放的资源,就不用定义next
指针了,并且,当申请空间的大小小于
4/8时我们将它扩成4/8来存储后面的地址


3. 定长池基础框架

//定长内存池
template<class T>
class ObjectPool
{
public:
	inline static void* SystemAlloc(size_t kpage)//向系统申请内存的函数
	{
#ifdef _WIN32
		void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
		// linux下brk mmap等
#endif

		if (ptr == nullptr)
			throw std::bad_alloc();
		return ptr;
	}
	T* New()
	{}

	void Delete(T* obj)
	{}

private:
	char* _memory = nullptr;     //向系统申请的一大块内存,申请了多少字节就从起始地址++多少次,所以定义为char*
	void* _freeList = nullptr;   //还回来的空间的过程中,链接的自由链表的头指针
	size_t _surplusBytes = 0;    //定长池剩余的空间大小(字节为单位)
};

关于代码的解释都在注释中,如果对架构
有不明白的地方欢迎私信.所以我们主要
是要实现两个函数,一个是new申请空间
一个是delete释放空间,并且需要注意的是,
这个定长池是一个类,在使用它时,同一类
型的变量申请空间会去同一个对象中申请


4. 定长池New的具体实现

在实现new函数之前我们要先想到
三个点:

  1. 当自由链表中有空间时,应该优先
    去自由链表中拿,而不是给它切分内存

  2. 当定长池剩余的空间不足时,需要扩容

  3. 若对象的大小小于4/8个字节,那么它
    就无法存储下一个位置的地址,此时需
    要将它直接扩为4/8字节来使用

T* New()
{
	T* obj = nullptr;
	//若有还回来的内存,优先使用它而不是去切割大内存
	if (_freeList != nullptr)
	{
		void* next = *(void**)_freeList;//先把头内存的后面一个内存找到,再把头内存头删了给外界用
		obj = (T*)_freeList;
		_freeList = next;
	}
	else
	{
		if (_surplusBytes < sizeof(T))//当剩余的空间不足以分配给T类型时,也要扩容
		{
			//_memory = malloc(128 * 1024);//128kbKB
			_surplusBytes = 128 * 1024;
			_memory = (char*)SystemAlloc(_surplusBytes);
			if (_memory == nullptr)
				throw std::bad_alloc();
		}
		obj = (T*)_memory;
		//若此对象的大小小于的指针的大小(4/8),则它释放后无法存储下一个内存的地址,要做特殊处理
		size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);
		_memory += objSize;
		_surplusBytes -= objSize;
	}
	//定位new,显示调用T的构造函数初始化
	new(obj)T;
	return obj;
}

5. 对代码的解释

对代码的解释:

  1. 自由链表部分

void* next = * (int**)_freeList;这段代码的意思就是将自由链表的头节点的下一个节点提取出来,由于使用的机器是32还是64位是不确定的,所以将freelist强转为int**后,再解引用就是int* 类型的变量,假如你是32位那么int* 就占四位,假如你是64位那么int* 就占八位,完全省略了去判断电脑是多少位的必要.并且将自由链表中的数据返还给外界使用是用的头删的方法,因为效率更快!

在这里插入图片描述

  1. 直接切分内存部分

假如在64位机器下,一个指针的大小是8字节,但是用户申请了4字节的空间,这时我们需要将空间扩为八字节再返回给用户,因为一旦这份空间被返回,四字节是无法当成指针使用存储下一个节点地址的!并且切分内存后,_memory要向后移动,避免将同一份内存切分给不同的变量.在申请完内存后,需要使用定位new来对已经开辟好的空间做初始化.

在这里插入图片描述

  1. 定长内存池的物理结剖析

将已经释放的空间挂在自由链表上是逻辑的结构,但是实际的物理结构是不管自由链表悬挂了多少个内存块,它们实际上都是连在一起的在定长池中的,只不过已经被使用了的空间在指针_memory前面,而未分配内存的空间在_memory后面!

在这里插入图片描述


6. 定长池delete的具体实现

定长池的删除比较简单,不需要将空间
free掉,只需要将空间挂到自由链表上即可
并且使用头插的方式

具体代码如下:

void Delete(T* obj)
{
	//显示调用析构函数
	obj->~T();
	//使用**强转,不管是32位还是64位都没问题
	//取obj对象头四个字节来存储nullptr或下一个被Delete的对象的空间的地址
	//使用头插,不用每次都去找尾
	*(void**)obj = _freeList;
	_freeList = obj;
}

7. 总结以及小tips

定长池的实现是在为后面的并发内存
池打基础,请同学们耐心掌握这篇文章
的所有内容,后面会有大用处!

小tips:

我们在向系统申请内存时,一次性申请了256K也就是256*1024个字节的空间,也就是说我们的定长池最多只能256K


🔎 下期预告:高并发内存池总体框架🔍

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

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

相关文章

对于双显卡电脑,如何分辨现在用的是独立显卡还是集成显卡?

一、问题描述 台式电脑本身自带了集成显卡&#xff0c;然后又购买了一块NVIDIA的独立显卡。 现在&#xff0c;就有疑问了&#xff0c;如何判断你的显示器连接的是独立显卡还是集成显卡呢&#xff1f; 二、NVIDIA双显卡机型 1、在桌面右下角&#xff0c;选择NVIDIA图标&…

HarmonyOS创建属性动画

属性动画的使用 1 概述 属性动画&#xff0c;是最为基础的动画&#xff0c;其功能强大、使用场景多&#xff0c;应用范围较广。常用于如下场景中&#xff1a; 一、页面布局发生变化。例如添加、删除部分组件元素。二、页面元素的可见性和位置发生变化。例如显示或者隐藏部分…

华为战略管理的核心工具与方法论:五看三定之“五看”怎么看

昨天&#xff0c;华研荟介绍了华为战略管理的一些基本概念和做法&#xff0c;今天就来为大家分享“五看三定”这个具体的方法。 首先来看一下什么叫做“五看三定”。 从名字我们可以看到&#xff0c;五看三定是一个缩略词&#xff0c;为了方便记忆。说的是在战略管理中的一些做…

公司销售技巧培训方案

公司销售技巧培训方案 一、引言 随着市场竞争的日益激烈&#xff0c;销售技巧对于公司的发展至关重要。为了提高销售人员的技能水平&#xff0c;提高销售业绩&#xff0c;本文将介绍一份公司销售技巧培训方案。该方案结合实际案例&#xff0c;通过系统性的培训&#xff0c;帮…

关于空间BN

批次归一化对每个神经元都进行了归一化&#xff0c;或者说对每个特征都进行了归一化&#xff0c;并且用可学习的参数和进行重构。 那么如果卷积神经网络有3个通道&#xff0c;长和宽都是244&#xff0c;BN将需要学习3*244*244*2个参数&#xff0c;计算量是不是太大了&#xff1…

UniGui使用CSS移动端按钮标题垂直

unigui移动端中按钮拉窄以后&#xff0c;标题无法垂直居中&#xff0c;是因为标题有一个padding属性&#xff0c;在四周撑开一段距离。会变成这样&#xff1a; 解决方法&#xff0c;用css修改padding&#xff0c;具体做法如下 首先给button的cls创建一个cls,例如 然后添加css&…

在IDEA中配置Git的Push键

前言 笔者在重新安装IDEA过程中需要重新绑定了Git&#xff0c;发现提交代码过程中push键消失了&#xff0c;所以笔者就以这篇文章记录一下Git配置push键的详细过程。 注意笔者当前IDEA版本为2019&#xff0c;可能和读者有所区别&#xff0c;但是操作思路是差不多的。 操作步…

【Axure RP9】元件应用(图文并茂)----含登入,个人简历案例

目录 : 一&#xff0c;元件基本介绍 1.1 元件概述 1.2 元件操作 1.3 快捷键大全 二&#xff0c;基本元件的应用 2.1 形状 2.2 图片 2.3 文本 2.4 线段原件 2.5 热区 2.5.1 热区应用 三, 表单型元件的应用 3.1 文本框 3.2 文本域 3.3 下拉列表 3.4 列表框 3.5 …

Profibus、Profinet、Ethernet的详细对比

PROFINET 是一种新的以太网通讯系统&#xff0c;是由西门子公司和 Profibus 用户协会开发。 PROFINET 具有多制造商产品之间的通讯能力&#xff0c;自动化和工程模式&#xff0c;并针对分布式智能自动化系统进行了优化。其应用结果能够大大节省配置和调试费用。 PROFINET 系统集…

geemap学习笔记026:如何循环加载影像集合中的每一景影像,筛选,并且进行导出

前言 影像集合中通常包含多景可用的影像&#xff0c;但是我们有时候需要查看经过某一个区域的每一景影像&#xff0c;然后筛选最适合的一景&#xff0c;今天就来实现这个操作。 1 导入库并显示地图 import ee import geemapee.Initialize()2 加载Landsat 8数据 # 应用尺度缩…

CSS的基本选择器及高级选择器(附详细示例以及效果图)

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍HTML中CSS的基础选择及高级选择器&#xff08;详解&#xff09;以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习记录获&#xf…

【计算思维】第14届蓝桥杯省赛计算思维U8组真题试卷

选择题 第 1 题 单选题 要把下面 4 张图片重新排列成蜗牛的画像&#xff0c;该如何排列这些图片?( ) A. B. C. D. 第 2 题 单选题 下图的几张牌&#xff0c;每次可以交换任意 2 张。 如将它们按照下面的顺序排列&#xff0c;最少需要交换( )次。 A.4 B.5 C.6 D.7 …

C++面向对象(OOP)编程-友元(友元函数和友元类)

本文主要介绍面向对象编程的友元的使用&#xff0c;以及友元的特性和分类&#xff0c;提供C代码。 1 为什么引进友元 面向对象编程&#xff08;OOP&#xff09;的三大特性中的封装&#xff0c;是通过类实现对数据的隐藏和封装。一般定义类的成员变量为私有成员&#xff0c;成员…

虚拟机/etc/fstab 变更只读模式ready-only处理

虚拟机误操作将/etc/fstab中的根目录注释掉了&#xff0c;重启虚机后虚机可以正常启动&#xff0c;但无法进行修改 # vi /etc/fstab 提示文件只读 无法进行操作 解决办法&#xff1a; 1、重启虚机&#xff0c;按e进入单用户模式 2、修改内核所在那行参数&#xff0c;将ro 修…

什么是FPGA原型验证?

EDA工具的使用主要分为设计、验证和制造三大类。验证工作贯穿整个芯片设计流程&#xff0c;可以说芯片的验证阶段占据了整个芯片开发的大部分时间。从芯片需求定义、功能设计开发到物理实现制造&#xff0c;每个环节都需要进行大量的验证。 现如今验证方法也越来越多&#xff…

力扣题:数字与字符串间转换-12.14

力扣题-12.14 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;442. 数组中重复的数据 解题思想&#xff1a;从字符串中能够正确提取数字即可 class Solution(object):def complexNumberMultiply(self, num1, num2):""":type num1:…

科技提升安全,基于YOLOv8全系列模型【n/s/m/l/x】开发构建商超扶梯场景下行人安全行为姿态检测识别系统

在商超等人流量较为密集的场景下经常会报道出现一些行人在扶梯上摔倒、受伤等问题&#xff0c;随着AI技术的快速发展与不断普及&#xff0c;越来越多的商超、地铁等场景开始加装专用的安全检测预警系统&#xff0c;核心工作原理即使AI模型与摄像头图像视频流的实时计算&#xf…

Flutter常用命令

一、环境安装 flutter --version 查看当前安装的flutter 版本 flutter upgrade 升级当前的flutter 版本 flutter doctor 检查环境安装是否完成 二、项目编译运行 flutter clean 清空build目录 flutter pub get 获取pub插件包 flutter run --设备名称 运行项目到指定设…

linux 内核同步互斥技术之原子变量

原子变量用来实现对整数的互斥访问&#xff0c;通常用来实现计数器。 例如&#xff0c;我们写一行代码把变量 a 加 1&#xff0c;编译器把代码编译成 3 条汇编指令。 &#xff08;1&#xff09;把变量 a 从内存加载到寄存器。 &#xff08;2&#xff09;把寄存器的值加 1。 &am…

CSDN新增的代码分析 一键注释新技能

一键注释 贴段Django代码 from django.urls import reversedef my_view(request):url reverse(index)return redirect(url) 贴段Vue代码 <template><div id"app"><input v-model.lazy"msg" type"text" name"" &g…