QT基于mmap文件映射机制实现的内存池方法总结

在现代计算机系统中,高效的内存管理对于程序性能有着至关重要的影响。尤其是在处理大量数据或频繁分配和释放小块内存的应用场景下,传统的内存分配方式(如malloc和free)可能会导致显著的性能开销和内存碎片化问题。为了克服这些问题,开发者们常常会设计并实现自定义的内存池(Memory Pool),以优化内存分配策略,提升应用程序的响应速度和稳定性。

本篇文章将聚焦于一种特殊的内存池实现——基于mmap文件映射机制的内存池。通过使用操作系统提供的mmap接口,我们可以将文件或者匿名段映射到进程的地址空间,从而获得一块连续的虚拟内存区域。这种方法不仅能够避免传统堆分配带来的碎片化问题,而且还可以利用文件系统的缓存机制来提高I/O效率,特别适合需要高效管理和访问大块内存的应用。

mmap文件映射机制介绍

mmap(Memory-Mapped File I/O)是一种在Unix-like操作系统(包括Linux和macOS)以及Windows中用于将文件或设备映射到进程的地址空间的技术。它允许程序直接对内存进行读写操作,而不是通过传统的I/O系统调用如read和write来访问文件内容。这种方式可以简化编程模型,同时提供更高效的I/O性能。
mmap文件映射机制

工作原理

当使用mmap时,操作系统会为应用程序分配一块虚拟内存区域,并将其与磁盘上的文件关联起来。这块内存看起来就像是一个普通的内存数组,但实际上它的内容是直接从文件加载进来的。任何对这块内存的修改都会反映到对应的文件上,或者根据具体的映射选项,在适当的时候同步回文件。

匿名映射:不与任何文件关联,通常用于创建共享内存段。
文件映射:将文件的内容映射到内存,使得文件的一部分或全部可以像内存一样被访问。

主要优点

提高I/O效率:减少了用户态和内核态之间的数据拷贝次数,因为数据可以直接在页缓存和应用之间传递。
简化编程接口:程序员可以通过指针直接访问文件内容,无需显式的读写操作。
支持大文件处理:对于非常大的文件,不需要一次性将整个文件载入内存,而是按需加载页面。
并发性:多个进程可以通过映射相同的文件来实现内存共享,从而方便地进行进程间通信。

代码实现

请注意,为了保持文章的简洁性和重点突出,文中仅粘贴了部分核心代码,完整实现请参阅项目的源代码库。这些代码片段旨在帮助理解内存池的工作原理和技术要点,而完整的功能和错误处理逻辑则包含在未展示的代码中。

初始化

在实现基于mmap的文件映射技术时,初始化阶段是至关重要的。它为后续的内存访问操作奠定了基础,并确保了系统资源能够被安全有效地使用。首先,需要选择一个合适的文件作为映射的目标,这个文件既可以是现有的,也可以是为了映射而特别创建的新文件。对于现有文件,程序必须以适当的权限打开该文件,以便执行读取、写入或同时进行这两种操作。接下来,调用操作系统提供的mmap函数(或其等价物),通过提供必要的参数如映射大小、保护标志、映射类型以及文件描述符来建立文件与进程地址空间之间的映射关系。成功的初始化不仅使得文件内容如同常规内存一样可直接访问,而且优化了I/O性能,减少了数据复制次数。

void MemoryPool::initializePool(size_t memSize_mb)
{//内存映射size_t memSize_bit = memSize_mb * 1024 * 1024;assert(std::numeric_limits<decltype(MemBlock::blockIndex)>::max() * 4 > memSize_bit && "over MaxMemSize!");QString cachePath = QStandardPaths::standardLocations(QStandardPaths::TempLocation).first();QDir dir(cachePath);if(!dir.exists())dir.mkpath(cachePath);QString fileUuid = QUuid::createUuid().toString(QUuid::WithoutBraces);m_virtualFile.reset(new QFile(QString("%1/%2.tmp").arg(cachePath, fileUuid)));if(m_virtualFile->open(QIODevice::ReadWrite | QIODevice::Truncate)){m_virtualFile->resize(memSize_bit);m_memoryPtr = m_virtualFile->map(0, memSize_bit, QFile::NoOptions);if(m_memoryPtr == nullptr){qDebug() << "fileMap failed:" << m_virtualFile->error() << "reason:" << m_virtualFile->errorString();assert(false);}m_blockCount = static_cast<size_t>(std::floor((double)memSize_mb * 1024.0 * 1024.0 / double(BLOCK_SIZE)));} else {qDebug() << "file open failed" << m_virtualFile->error() << "reason:" << m_virtualFile->errorString();assert(false);}
}

申请内存

内存分配是通过allocate成员函数实现的,它遵循一种紧凑的内存布局策略,旨在最小化碎片并最大化连续可用内存。当请求一定数量的字节时,allocate会计算所需的块数目,并尝试找到一段足够大的连续空闲区域来满足请求。算法首先检查是否可以在当前已使用的最高地址之后添加新的分配,如果不行,则遍历已使用的块列表,寻找可以插入新分配的间隙。一旦确定了合适的起始位置,就更新内部数据结构(如m_useBlocks),记录下新分配的块信息,并返回指向这块内存的指针。这种分配机制不仅保证了高效利用内存资源,而且还支持灵活的内存管理需求,例如动态增长的应用场景。

void* MemoryPool::allocate(size_t memSize_bit) 
{size_t takeBlockSize = static_cast<size_t>(std::ceil(memSize_bit / double(BLOCK_SIZE)));//判断是否还有适用的空闲内存size_t allocIndex = -1;if(!m_useBlocks.empty()) {const MemBlock &useBlock = *m_useBlocks.rbegin();if(m_blockCount - useBlock.blockIndex + useBlock.blockSize > takeBlockSize) { //TODO >=allocIndex = useBlock.blockIndex + useBlock.blockSize;} else {//从碎片中找size_t startIndex = 0;for(auto iter = m_useBlocks.begin(); iter != m_useBlocks.end(); ++iter) {size_t freeBlockSize = iter->blockIndex - startIndex;if(freeBlockSize >= takeBlockSize) {allocIndex = startIndex;break;}startIndex += iter->blockIndex + iter->blockSize;}}} else {if(m_blockCount >= takeBlockSize)allocIndex = 0;}assert(allocIndex >= 0);assert(allocIndex < m_blockCount);//记录m_useBlocks.emplace(MemBlock(allocIndex, takeBlockSize));return getMemPtr(allocIndex);
}

回收申请的内存

内存的释放由deallocate成员函数处理,它是内存池管理中的重要组成部分,负责回收不再需要的内存块,以便它们可以被重新分配给未来的请求。当接收到一个指向先前分配内存的指针时,deallocate会根据该指针计算出对应的块索引,并从记录已使用块的数据结构中移除相应的条目。这样做既恢复了内存池的状态,也使得这些内存块再次变为可分配状态。此外,cllocate成员函数提供了一种更激进的方式清空整个内存池,即清除所有当前使用的块,使内存池回到刚初始化后的状态。正确的内存释放逻辑是防止内存泄漏的关键,同时也有助于维持应用程序的稳定性和响应速度。

void MemoryPool::deallocate(void *ptr) 
{int index = getSizeIndex(ptr);if(index < 0)return;MemBlock block(index, 0);auto iter = m_useBlocks.find(block);assert(iter != m_useBlocks.end());m_useBlocks.erase(iter);
}

后续扩展

1.利用自由链表和位图等数据结构来追踪空闲内存块的状态。这将有助于减少内存碎片并提高分配和释放操作的效率。
2.通过锁优化、无锁数据结构或分段锁等技术,减少多线程环境下的竞争开销,从而提升并发性能。
3.利用动态页扩展算法、预分配与懒加载、内存回收与重用机制等,升级为动态扩展页的方式,以无限扩大可用内存
4.基于此内存池、模板、对象池设计原则等技术,开发对象池,解决某些高频发构造的对象导致性能降低的问题场景

总结

通过基于mmap文件映射机制的内存池实现,我们显著优化了内存分配效率并减少了碎片化问题,提升了应用程序的性能和稳定性,解决了大数据量占用内存量高的问题。

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

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

相关文章

车载DoIP诊断框架 --- 连接 DoIP ECU/车辆的故障排除

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…

0—QT ui界面一览

2025.2.26&#xff0c;感谢gpt4 1.控件盒子 1. Layouts&#xff08;布局&#xff09; 布局控件用于组织界面上的控件&#xff0c;确保它们的位置和排列方式合理。 Vertical Layout&#xff08;垂直布局&#xff09; &#xff1a;将控件按垂直方向排列。 建议&#xff1a;适…

普中单片机-51TFT-LCD显示屏(1.8寸 STM32)

普中官方论坛&#xff1a; http://www.prechin.cn/gongsixinwen/208.html 普中科技-各型号开发板资料链接&#xff1a;https://www.bilibili.com/read/cv23681775/?spm_id_from333.999.0.0 27-TFTLCD显示实验_哔哩哔哩_bilibili 2.程序烧录 2.1设置彩屏驱动 3.实验效果

嵌入式开发工程师笔试面试指南-Linux系统移植

1 Linux内核启动流程 引导加载阶段 计算机通电后&#xff0c;首先由 BIOS 或 UEFI 进行初始化&#xff0c;完成硬件自检等操作。 找到可启动设备&#xff0c;读取其第一个扇区的 MBR&#xff0c;MBR 中的引导加载程序&#xff08;如 GRUB&#xff09;被加载到内存并运行。 内…

图扑数字孪生:解锁压缩空气储能管控新高度

​在能源转型的关键时期&#xff0c;压缩空气储能凭借其独特优势&#xff0c;成为解决可再生能源间歇性问题、保障可靠能源供应的重要技术。图扑软件&#xff08;Hightopo&#xff09;充分发挥其在 Web 2D&3D 可视化领域的技术专长&#xff0c;打造出先进的数字孪生压缩空气…

Redis 高可用性:如何让你的缓存一直在线,稳定运行?

&#x1f3af; 引言&#xff1a;Redis的高可用性为啥这么重要&#xff1f; 在现代高可用系统中&#xff0c;Redis 是一款不可或缺的分布式缓存与数据库系统。无论是提升访问速度&#xff0c;还是实现数据的高效持久化&#xff0c;Redis 都能轻松搞定。可是&#xff0c;当你把 …

AI 编码 2.0 分析、思考与探索实践:从 Cursor Composer 到 AutoDev Sketch

在周末的公司【AI4SE 效能革命与实践&#xff1a;软件研发的未来已来】直播里&#xff0c;我分享了《AI编码工具 2.0 从 Cursor 到 AutoDev Composer》主题演讲&#xff0c;分享了 AI 编码工具 2.0 的核心、我们的思考、以及我们的 AI 编码工具 2.0 探索实践。 在这篇文章中&am…

Qt Creator + CMake 构建教程

此教程基于: Qt 6.7.4Qt Creator 15.0.1CMake 3.26.4 Qt 6 以下的版本使用 CMake 构建可能会存在一些问题. 目录 新建窗体工程更新翻译添加资源软件部署(Deploy) 此教程描述了如何一步步在 Qt Creator 中使用 CMake 构建应用程序工程. 涉及 新建窗体工程, 更新翻译, 添加资源, …

锂电池保护板测试仪:电池安全的守护者与创新驱动力

在新能源产业蓬勃发展的今天&#xff0c;锂电池以其高能量密度、长循环寿命和环保特性&#xff0c;成为电动汽车、无人机、便携式电子设备等领域不可或缺的能量来源。然而&#xff0c;锂电池的安全性和稳定性一直是行业关注的焦点。为了确保锂电池在各种应用场景下的可靠运行&a…

岳阳市美术馆预约平台(小程序论文源码调试讲解)

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

【Java】I/O 流篇 —— 转换流与序列化流

目录 转换流原理InputStreamReader 转换输入流构造方法代码示例 OutputStreamWriter 转换输出流构造方法代码示例 练习 序列化流序列化流反序列化流**serialVersionUID**基本概念作用使用方式transient 关键字注意事项 转换流 原理 转换流属于字符流&#xff0c;是字符流和字节…

Mac 版 本地部署deepseek ➕ RAGflow 知识库搭建流程分享(附问题解决方法)

安装&#xff1a; 1、首先按照此视频的流程一步一步进行安装&#xff1a;(macos版&#xff09;ragflowdeepseek 私域知识库搭建流程分享_哔哩哔哩_bilibili 2、RAGflow 官网文档指南&#xff1a;https://ragflow.io 3、RAGflow 下载地址&#xff1a;https://github.com/infi…

计算机三级网络技术备考

#subtotal 1Mbps1024kb128KB12.8M/s #1024B1KB 1024KB1MB 1024MB1GB #路由器的5G信号和平常的波长不同&#xff08;5G的穿墙性能差&#xff09; #局域网LAN&#xff08;一公里内——构成集线机、交换机、同轴电缆&#xff09; #城域网MAN&#xff08;几公里到几十公里——光…

IDEA 2024.1 最新永久可用(亲测有效)

今年idea发布了2024.1版本&#xff0c;这个版本带来了一系列令人兴奋的新功能和改进。最引人注目的是集成了更先进的 AI 助手&#xff0c;它现在能够提供更复杂的代码辅助功能&#xff0c;如代码自动补全、智能代码审查等&#xff0c;极大地提升了开发效率。此外&#xff0c;用…

30 分钟从零开始入门 CSS

前言 最近也是在复习&#xff0c;把之前没写的博客补起来&#xff0c;之前给大家介绍了 html&#xff0c;现在是 CSS 咯。 30分钟从零开始入门拿下 HTML_html教程-CSDN博客 一、CSS简介&#xff1a;给网页“化妆”的神器 CSS&#xff08;层叠样式表&#xff09;就像“化妆“&a…

Game Maker 0.11更新:构建社交竞速游戏并增强玩家互动

在这三部分系列中&#xff0c;我们将介绍如何实现Game Maker 0.11中一些最激动人心的新功能。 欢迎来到我们系列文章的第一篇&#xff0c;重点介绍了The Sandbox Game Maker 0.11更新中的新特性。 The Sandbox Game Maker 0.11是一个多功能工具&#xff0c;帮助创作者通过游戏…

软件供应链安全工具链研究系列——RASP自适应威胁免疫平台(上篇)

1.1 基本能力 RASP是一种安全防护技术&#xff0c;运行在程序执行期间&#xff0c;使程序能够自我监控和识别有害的输入和行为。也就是说一个程序如果注入或者引入了RASP技术&#xff0c;那么RASP就和这个程序融为一体&#xff0c;使应用程序具备了自我防护的能力&#xff0c;…

2024信息技术、信息安全、网络安全、数据安全等国家标准合集共125份。

2024信息技术、信息安全、网络安全、数据安全等国家标准合集&#xff0c;共125份。 一、2024信息技术标准&#xff08;54份&#xff09; GB_T 17966-2024 信息技术 微处理器系统 浮点运算.pdf GB_T 17969.8-2024 信息技术 对象标识符登记机构操作规程 第8部分&#xff1a;通用…

HTTP与网络安全

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、HTTPS和HTTP有怎样的区别呢&#xff1f;HTTPS HTTP SSL/TLS&#xff08;SSL或者TLS&#xff09; HTTP&#xff1a;应用层 SSL/TLS&#xff1a;协议中间层 …

ASP.NET Core 8.0学习笔记(二十八)——EFCore反向工程

一、什么是反向工程 1.原则&#xff1a;DBFirst 2.反向工程&#xff1a;根据数据库表来反向生成实体类 3.生成命令&#xff1a;Scaffold-DbContext ‘连接字符串’ 字符串示例&#xff1a; Server.;DatabaseDemo1;Trusted_Connectiontrue; MultipleActiveResultSets true;Tru…