C++项目——高并发内存池(1)--介绍及定长内存池
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;
}

相关文章:
C++项目——高并发内存池(1)--介绍及定长内存池
1.什么是内存池 1.1 池化技术 将程序中需要经常使用的核心资源先申请出来,放在一个池内,由程序自己管理,这样可以提高资源的使用效率,也可以保证本程序占有的资源数量。 比如之前博文实现的线程池,就是预先的申请出…...
网络有线无线配置
一、需求 在无线接入区内,当Lsw1的上联口出现故障时,需要通过AP1-LSw1-LSw2-LSw3的路径访问公网server3。这是因为AP1通过无线网连接到LSw1,而LSw1与LSw3之间的链路出现故障,无法直接访问公网server3。因此,流量需要通…...
阅读笔记——Feature-Point Matching for Aerial and Ground Images方法
阅读笔记——Feature-Point Matching for Aerial and Ground Images 1.应用背景 在三维重建应用中,常用的是航空影像,但在城市环境中,面对大量的高层建筑物,航空影像往往不能很好的重建出完整的三维模型。因为航空影像视角约束以…...
前端工程师面试题10条必会笔试题
布局 左边20% 中间自适应 右边200px 不能用定位答案:圣杯布局/双飞翼布局或者flex什么叫优雅降级和渐进增强?渐进增强 progressive enhancement:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进…...
【技术分享】Web自动化之Selenium安装
Web 应用程序的验收测试常常涉及一些手工任务,例如打开一个浏览器,并执行一个测试用例中所描述的操作。但是手工执行的任务容易出现人为的错误,也比较费时间。因此,将这些任务自动化,就可以消除人为因素。Selenium 可以…...
【Linux】进程状态的理解
✍作者:阿润菜菜 📖专栏:Liunx系统编程 本文目录概述两个先行概念我们为啥创建进程Linux下的进程状态1. R 运行状态2.S 休眠状态 --- 可中断休眠状态3. D 磁盘休眠状态 ---不可中断休眠4.T 暂停状态 (t 追踪暂停状态)5…...
打游戏哪种蓝牙耳机比较好?适合玩游戏的无线蓝牙耳机
2023年耳机市场一如既往地卷,不只是卷音质,还在外观和功能上做了许多的改进,以至于现在哪怕不懂耳机的人从各电商平台都能闭眼入一个款平价品牌耳机且极少会踩雷,玩游戏是当前年轻人的娱乐方式,下面整理了几款适合玩游…...
Fortinet推出新一代自研安全芯片,跨所有网络边缘加速网络与安全融合
专注网络与安全融合的全球网络安全领导者 Fortinet(NASDAQ:FTNT),近日宣布推出新一代自研安全芯片 FortiSP5,作为 Fortinet ASIC 技术的最新突破,有力推动了分布式网络边缘安全的重大飞跃。FortiSP5 源自 F…...
ChatGPT爆火背后的真相:学编程已经成为必选项
这一阵最热门的话题,莫过于人工智能新选手——ChatGPT,在推出后只用了两个月就积累了1亿用户!它的出现在科技圈掀起了一阵“惊涛骇浪”,有人称ChatGPT的意义,堪比当年蒸汽机的出现,它足以爆发新一轮的“工业…...
Unity UI框架
一、简介 最近在各大网站看了一下 Unity3d 的 UI 框架,各种 UI 框架已经有很多的版本了,各有千秋,有的功能虽然写的完善,但用起来太复杂,有的框架功能不完善,搞个课程就上架了,还有什么 MVC 框…...
vue2提取vue-router的title单独存放,使用i18n实现
成品效果 首先引入i18n(vue-i18n官网文档) 依赖包 npm install vue-i18n8然后单独在src目录下新建一个文件夹lang,存放相对应的变量名称,我这里只做显示中文所以其他引入我都注释了,具体目录如下: src\lang/zh.js部分代码 export…...
【Linux操作系统】【综合实验三 用户帐号、文件系统与系统安全管理】
文章目录一、实验目的二、实验要求三、实验内容四、实验报告要求一、实验目的 要求掌握Linux系统用户的创建、删除与管理操作;熟悉Linux文件系统的管理模式,学会创建用户文件系统并装载和卸载文件系统;掌握超级用户的管理方式与权限…...
sqlite3数据库-sqlite语句1(五)
DML(Data Manipulation Language,数据操作语言) SELECT:查询表中的数据;SELECT语句中使用WHERE子句SELECT <列名>,... FROM <表名> WHERE <条件表达式>;SELECT id,name,purchase_price FROM Product; /*使用逗号分隔查询多列,顺序同子句顺序*/ SELECT * FROM…...
【图像分类】卷积神经网络之LeNet5网络模型实现MNIST手写数字识别
写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 在上一篇博文中我们对LeNet5网络模型的结构进行了剖析,本篇博文,我们将使用PyTorch搭建LeNet5实现MNIST手写数字…...
前端开发环境搭建
文章目录Node.js是什么安装查看版本入门示例NPM使用 npm 命令安装模块常见命令使用淘宝 NPM 镜像TypeScript安装入门示例从github拉取构建项目如何从零创建一个TypeScript项目规划目录结构新建项目Web App运行服务添加依赖打包使用browserify打包使用webpack打包推荐流程目录配…...
学习Flask之四、网页表单
第二章介绍的request对象,使用了客户端请求的所有信息。特别地,request.form提供了对POST请求提交的表单数据的访问。尽管Flask请求对象的支持足于处理网页单,但是还有很多作务很繁锁和重复。两个很好的例子是产生HTML表单代码和验证表单数据…...
CenterMask paper笔记
CenterMask是一个anchor free的实例分割模型, 来自paper: CenterMask: Real-Time Anchor-Free Instance Segmentation 提起anchor free, 会想到FCOS模型,是用来目标检测的, 那么这里就用到了FCOS, 不过换了backbone, 在FCOS检测出目标框后&…...
06- OpenCV查找图像轮廓 (OpenCV基础) (机器视觉)
知识重点 灰度图转换: gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)二值化: 返回两个东西,一个阈值, 一个是二值化的图: thresh, binary cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)查找轮廓: 返回两个结果,分别是轮廓和层级: c…...
OpenGL学习日记之模型绘制
自己编译运行过程中遇到的一些问题 下载Assimp已编译的lib(因为我们公司的电脑有很多权限和限制,也不能自己安装一些没有报备的软件,所以愁方便我就没有用cMake自己编译了)找到一位免费分享的博主的。 https://blog.csdn.net/lady_killer9/article/deta…...
Springboot接口多个DTO入参的Postman上传方式
在Java中使用Spring Boot框架时,可以同时使用多个DTO作为方法参数。 TO(Data Transfer Object)是一个常见的设计模式,用于封装数据传输对象。它通常用于将数据从一个层传递到另一个层,例如将数据从服务层传递到控制器…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
