C++项目——高并发内存池(1)--介绍及定长内存池

news/2024/5/19 3:24:26/文章来源:https://blog.csdn.net/qq_68741368/article/details/129050777

 1.什么是内存池

1.1 池化技术

将程序中需要经常使用的核心资源先申请出来,放在一个池内,由程序自己管理,这样可以提高资源的使用效率,也可以保证本程序占有的资源数量。

比如之前博文实现的线程池,就是预先的申请出来一些线程,当有任务被推送到任务队列时,线程池内的线程立即开始处理任务,不需要在程序内一次次的创建线程、关闭线程等。

1.2 内存池

普通场景下,当程序长时间运行,或多或少有相关内容会去申请内存资源,由于这些申请的内存块大小不一,会造成大量的内存碎片从而降低程序和操作系统的效率。内存池就是在使用内存空间之前,先整体申请分配一大块内存(内存池),当需要申请内存时,从内存池中取出一块进行动态分配,当该内存被释放时,将释放过后的内存在放回池内,并尽量与周边的空闲内存块合并,以减少外内存碎片,重复利用。若内存池不够时,则自动扩大内存池,从操作系统中申请更大的内存池。

1.2.1 内存池的意义

有两个好处:

  1、由于向内存申请的内存块都是比较大的,所以能够降低外碎片问题。

  2、一次性向内存申请一块大的内存慢慢使用,避免了频繁的向内存请求内存操作,提高内存分配的效率。

1.2.2 内存碎片

外内存碎片

系统经过一系列的分配内存和回收内存,当遇到需要分配一大块内存空间时,剩余内存空间的总数够,但是内存空间不连续,导致无法分配。

内内存碎片

内部碎片是指一个已分配的块比有效载荷大时发生的。(假设以前分配了10个大小的字节,现在只用了5个字节,则剩下的5个字节就会内碎片)。内部碎片的大小就是已经分配的块的大小和他们的有效载荷之差的和。因此内部碎片取决于以前请求内存的模式和分配器实现的模式。

2. 定长内存池

在实现高并发内存池之前,先写一个假定每次申请的内存空间都是固定值的内存池,即定长内存池

介绍:

实现一个 FreeList,每个 FreeList 用于分配固定大小的内存块,比如用于分配 32字节对象的固
定内存分配器等,使用模板,使定长内存池可以根据分配的对象而发生改变。定长内存池中有两个指针, _memory和_freeList 。_memory是指向大块内存的指针,当需要分配内存的时候,他根据(模板类型)字节数,向后移动,将该块内存分配出去。_freeList是指向由还回的内存块的链接而成的链表的头指针。分配时如果内存池中剩余不够,我们就再创建一个新的大块,不必保存前一块大内存的地址,因为如果它始终被占用,申请空间时就用不上它,如果它后续被释放,也会进入_freeList,供我们再次使用。

2.1 定长内存池——回收内存

先上框架

template<class T>
class ObjectPool
{
public://分割内存块 T* New(){}//回收内存void Delete(T* obj) //obj所指向的这一块空间被回收了{}private:char* _memory = nullptr;//指向大块内存的指针size_t _remainBytes = 0;//大块内存存在切分过程中的剩余字节数void* _freeList = nullptr;//还回来的内存块由_freeList指针指向的链表链接起来	
};

由于每个内存块回收后都是放到_freeList里面,形成一个单链表。所以内存块中的起始一部分空间需要变成 一个指针,指向下一个节点。

  • 情况一:当单链表为空的时候,回收一个内存块。首先需要将当前内存块中的第一个指针大小的空间置成nullptr。然后使指向当前内存块的指针作为单链表的头结点。
  • 情况二:当单链表不为空的时候,回收一个内存块。首先如果使用尾插法,那么每次都需要遍历一遍链表,时间复杂度高,所以我们采用头插法。先让当前内存块中的第一个指针大小的空间的置成单链表的头结点,然后将头结点赋给指向当前内存块的指针

问题:我们要想将归还的内存块使用一张链表链接起来,那么这个内存块一定需要大于一个指针的大小!不然我们无法修改内存块的前指针变量个大小的字节,使其指向下一块归还的内存。

但是在32位和64位平台下,指针的大小是不一致的。我们怎么知道使用者处于哪个平台?

针对于这一问题,本质是由于指针变量的大小不确定。但是真正处于某一平台下,指针的大小是唯一的。所以我们不应该显示的规定将前4个或8个字节进行修改,而应该使用一种可以依平台而定的修改方案,即按照当前平台的指针变量大小作为需要修改的字节数。

 采用解引用 (void**)obj || (int**)obj || || (char**)obj

 而非依平台而定解引用 (int*)obj || (long long*)obj

    void Delete(T* obj) //obj所指向的这一块空间被回收了{/*	if (_freelist == nullptr){*(void**)obj = nullptr;_freelist = obj;}else{*(void**)obj = _freelist;_freelist = obj;}发现_freeList是空的时候依然符合 else 所以合并一下*///要回收了 拿析构函数清理一下 obj->~T();* (void**)obj = _freeList;_freeList = obj;}

2.2 定长内存池——分割内存

template<class T>
class ObjectPool
{
public:T* New(){//当剩余的字节数小于需要的字节数,就重新申请一个新的大空间T* obj = nullptr;//优先使用换回来的内存块对象,重复利用if (_freeList){void* next = *(void**)_freeList;//_freeList的前指针大小个字节 解引用为nextobj = (T*)_freeList;_freeList = next;}else{if (_remainBytes < sizeof(T)){_remainBytes = 128 * 1024;_memory = (char*)malloc(128 * 1024);if (_memory == nullptr){throw std::bad_alloc();}}obj = (T*)_memory;_memory += sizeof(T);_remainBytes -= sizeof(T);}//空间有了 初始化一下 定位newnew(obj)T;return obj;}//归还内存void Delete(T* obj){}
private:char* _memory = nullptr;//指向大块内存的指针size_t _remainBytes = 0;//大块内存存在切分过程中的剩余字节数void* _freeList = nullptr;//还回来的内存块由_freeList指针指向的链表链接起来	
};

2.3 测试代码及结果

struct TreeNode
{int _val;TreeNode* _left;TreeNode* _right;TreeNode():_val(0), _left(nullptr), _right(nullptr){}
};void TestObjectPool()
{// 申请释放的轮次const size_t Rounds = 5;// 每轮申请释放多少次const size_t N = 100000;std::vector<TreeNode*> v1;v1.reserve(N);size_t begin1 = clock();//记录使用new和delete场景下的开始时间for (size_t j = 0; j < Rounds; ++j){for (int i = 0; i < N; ++i){v1.push_back(new TreeNode);}for (int i = 0; i < N; ++i){delete v1[i];}v1.clear();}size_t end1 = clock();std::vector<TreeNode*> v2;v2.reserve(N);ObjectPool<TreeNode> TNPool;size_t begin2 = clock();//记录使用定长内存池的New和Delete的开始时间for (size_t j = 0; j < Rounds; ++j){for (int i = 0; i < N; ++i){v2.push_back(TNPool.New());}for (int i = 0; i < N; ++i){TNPool.Delete(v2[i]);}v2.clear();}size_t end2 = clock();cout << "new cost time:" << end1 - begin1 << endl;cout << "object pool cost time:" << end2 - begin2 << endl;
}

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

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

相关文章

第四次作业

学生表&#xff1a;Student (Sno, Sname, Ssex , Sage, Sdept)学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键课程表&#xff1a;Course (Cno, Cname)课程号&#xff0c;课程名 Cno为主键学生选课表&#xff1a;SC (Sno, Cno, Score)学号&…

面试经常被问悲观锁和乐观锁?什么是cas?来我花3分钟时间告诉你

锁大家都知道吧&#xff0c;多线程访问资源会存在竞争&#xff0c;那么就需要加锁进而让多个线程一个一个访问。 比如有一个房间&#xff0c;一次只能进一个人&#xff0c;现在有十个人都想进去怎么办&#xff1f; 对&#xff0c;加锁。拿一把钥匙&#xff0c;谁抢到钥匙谁就…

微服务 ModuleFederationPlugin Vue项目体验

随着公司项目的模块越来越多&#xff0c;每次打包后的项目都非常大&#xff0c;而且每修改一个小的模块&#xff0c;都会将整个项目打包&#xff0c;会非常的麻烦&#xff0c;随着前端的发展&#xff0c;微服务的出现&#xff0c;很好的解决了项目庞大的问题&#xff0c;而且每…

大数据处理学习笔记1.4 掌握Scala运算符

文章目录零、本讲学习目标一、运算符等价于方法&#xff08;一&#xff09;运算符即方法&#xff08;二&#xff09;方法即运算符1、单参方法2、多参方法3、无参方法二、Scala运算符&#xff08;一&#xff09;运算符分类表&#xff08;二&#xff09;Scala与Java运算符比较三、…

I.MX6ULL_Linux_系统篇(17) uboot分析-启动linux

bootz 启动 Linux 内核 images 全局变量 不管是 bootz 还是 bootm 命令&#xff0c;在启动 Linux 内核的时候都会用到一个重要的全局变量&#xff1a;images&#xff0c; images 在文件 cmd/bootm.c 中有如下定义&#xff1a; images 是 bootm_headers_t 类型的全局变量&…

03- 通过OpenCV进行图像变换 (OpenCV基础) (机器视觉)

知识重点 resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) 图像的放大与缩小, 变形 flip(src, flipCode) 图像的翻转 rotate(img, rotateCode) 图像的旋转 warpAffine(src, M, dsize, flags, mode, value) 仿射变换是图像旋转, 缩放, 平移的总称.具体的做法是通…

Windows10神州网信政府版麦克风、摄像头的使用

Windows10神州网信政府版默认麦克风摄像头是禁用状态&#xff0c;此禁用状态符合版本规定。 在录课和直播过程中&#xff0c;如果需要使用麦克风和摄像头的功能&#xff0c;可以这样更改&#xff1a; 1、鼠标右键点击屏幕左下角的开始菜单图标&#xff0c;选择windows中的“运…

跨时钟域 单脉冲 脉冲信号同步问题——快到慢(1)

引言 FPGA设计或者ASIC设计中经常存在多个时钟域&#xff0c;那么这些时钟域之间脉冲信号的同步该如何进行设计&#xff1f;快时钟域到慢时钟域的脉冲信号同步与慢时钟域信号到快时钟域信号的同步是不一样的。 本文先给出快时钟域到慢时钟域脉冲信号同步的方法之一&#xff…

C++设计模式(17)——备忘录模式

亦称&#xff1a; 快照、Snapshot、Memento 意图 备忘录模式是一种行为设计模式&#xff0c; 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。 问题 假如你正在开发一款文字编辑器应用程序。 除了简单的文字编辑功能外&#xff0c; 编辑器中还要有设置文本格…

覃小龙34岁生日记:结合趋势,发挥优势,方能百战不殆

覃小龙34岁生日记:结合趋势&#xff0c;发挥优势&#xff0c;方能百战不殆&#xff01;2023-2-20星期一 覃小龙2023年2月17日&#xff0c;是我34岁生日&#xff0c;1989年出生的我&#xff0c;一晃眼&#xff0c;已经走过第34个年头了&#xff01;从2016年创业到今天&#xff0…

【Spark分布式内存计算框架——Spark SQL】13. 自定义UDF函数

第七章 自定义UDF函数 无论Hive还是SparkSQL分析处理数据时&#xff0c;往往需要使用函数&#xff0c;SparkSQL模块本身自带很多实现公共功能的函数&#xff0c;在org.apache.spark.sql.functions中。SparkSQL与Hive一样支持定义函数&#xff1a;UDF和UDAF&#xff0c;尤其是U…

C++进阶:二叉搜索树

文章目录1 二叉搜索树概念2 二叉搜索树的实现2.1 结点的定义2.2 二叉搜索树的插入2.2 二叉搜索树的查找2.3 二叉搜索树的删除2.4 二叉搜索树的默认成员函数2.4.1 拷贝构造2.4.2 析构函数2.4.3 赋值重载3 二叉搜索树的应用3.1 k模型3.2 kv模型4 二叉搜索树的性能分析1 二叉搜索树…

Nacos注册中心和配置中心使用详情

Nacos Nacos就是Alibaba推出的一款 配置中心和注册中心结合的一款工具&#xff0c;属于SpringCloudAlibaba技术栈下 Nacos官网地址 https://nacos.io/zh-cn/index.html 安装启动 下载 目录结构 根据目录结构可以看出Nacos本身也就是一个java程序。SpringBoot程序 启动 c…

配置Tomcat性能优化

配置Tomcat性能优化 &#x1f4d2;博客主页&#xff1a; 微笑的段嘉许博客主页 &#x1f4bb;微信公众号&#xff1a;微笑的段嘉许 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐留言&#x1f4dd; &#x1f4cc;本文由微笑的段嘉许原创&#xff01; &#x1f4…

【Python网络编程】利用Python进行TCP、UDP套接字编程

之前实现了Java版本的TCP和UDP套接字编程的例子&#xff0c;于是决定结合Python的学习做一个Python版本的套接字编程实验。 流程如下&#xff1a; 1.一台客户机从其标准输入&#xff08;键盘&#xff09;读入一行字符&#xff0c;并通过其套接字将该行发送到服务器。 2.服务…

基于springboot+vue的校园社团管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

基于 DSP+FPGA 的高清图像跟踪系统研制

目标识别与跟踪技术是目前图像处理研究的重点方向&#xff0c;在军事和民用领域中 具有广泛的应用价值&#xff0c;如精确制导武器、导弹飞机预警等军事领域&#xff0c;如交通管理、 刑事侦查等民用领域。其中&#xff0c;如何在复杂的背景中&#xff0c;提取、识别与跟踪特定…

(考研湖科大教书匠计算机网络)第五章传输层-第八节2:TCP连接管理实践部分

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 此部分为补充内容&#xff0c;主要使用Java实现TCP和UDP通信 一&#xff1a;UDP通信 &#xff08;1&#xff09;Java数据报套接字通信模型 Java UDP通信模型&#xff…

健身房健身需要什么装备,五款健身房必备运动耳机分享

很多小伙伴第一次去健身房不知道选择什么运动装备&#xff0c;经常去健身房健身的小伙伴一般都使用哪一些专业的运动耳机呢&#xff1f;下面这几款运动蓝牙耳机&#xff0c;都是观察身边经常去健身房的人佩戴的&#xff0c;分享给大家&#xff0c;看看有没有帮助到你。 1、南卡…

从交换机安全配置看常见局域网攻击

前言 构建零信任网络&#xff0c;自然离不开网络准入(NAC)&#xff0c;这就涉及到交换机的一些安全测试&#xff0c;于是有了此文《从交换机安全配置看常见局域网攻击》。 交换机安全配置 如本文标题所说从交换机安全配置看常见的局域网攻击&#xff0c;那么下面提到的各种攻…