网站首页 > 技术文章 正文
引言
C/C++下内存管理是让几乎每一个程序员头疼的问题,分配足够的内存、追踪内存的分配、在不需要的时候释放内存——这个任务相当复杂。而直接使用系统调用malloc/free、new/delete进行内存分配和释放,有以下弊端:
- 调用malloc/new,系统需要根据“最先匹配”、“最优匹配”或其他算法在内存空闲块表中查找一块空闲内存,调用free/delete,系统可能需要合并空闲内存块,这些会产生额外开销
- 频繁使用时会产生大量内存碎片,从而降低程序运行效率
- 容易造成内存泄漏
内存池(memory pool)是代替直接调用malloc/free、new/delete进行内存管理的常用方法,当我们申请内存空间时,首先到我们的内存池中查找合适的内存块,而不是直接向操作系统申请,优势在于:
- 比malloc/free进行内存申请/释放的方式快
- 不会产生或很少产生堆碎片
- 可避免内存泄漏
内存池设计
看到内存池好处这么多,是不是恨不能马上抛弃malloc/free,投奔内存池的怀抱呢?且慢,在我们自己动手实现内存池之前还需要明确以下几个问题:
- 内存池的空间如何获得?是程序启动时分配一大块空间还是程序运行中按需求分配?
- 内存池对到来的内存申请,有没有大小的限制?如果有,最小可申请的内存块为多大,最大的呢?
- 如何合理设计内存块结构,方便我们进行内存的申请、追踪和释放呢?
- 内存池占用越多空间,相对应其他程序能使用的内存就越少,是否要设定内存池空间的上限?设定为多少合适呢?
带着以上问题,我们来看以下一种内存池设计方案。
内存池实现方案一
从这里下载该内存池实现的源码:
https://link.zhihu.com/?target=https%3A//github.com/bangerlee/mempool
首先给出该方案的整体架构,如下:
图1.内存池架构图
结构中主要包含block、list 和pool这三个结构体,block结构包含指向实际内存空间的指针,前向和后向指针让block能够组成双向链表;list结构中free指针指向空闲 内存块组成的链表,used指针指向程序使用中的内存块组成的链表,size值为内存块的大小,list之间组成单向链表;pool结构记录list链表的头和尾。
内存跟踪策略
该方案中,在进行内存分配时,将多申请12个字节,即实际申请的内存大小为所需内存大小+12。在多申请的12个字节中,分别存放对应的list指针(4字节)、used指针(4字节)和校验码(4字节)。通过这样设定,我们很容易得到该块内存所在的list和block,校验码起到粗略检查是否出错的作用。该结构图示如下:
图2.内存块申请示意图
图中箭头指示的位置为内存块真正开始的位置。
内存申请和释放策略
申请:根据所申请内存的大小,遍历list链表,查看是否存在相匹配的size;
存在匹配size:查看free时候为NULL
free为NULL:使用malloc/new申请内存,并将其置于used所指链表的尾部
free不为NULL:将free所指链表的头结点移除,放置于used所指链表的尾部
不存在匹配size:新建list,使用malloc/new申请内存,并将其置于该list的used所指链表尾部
返回内存空间指针
释放:根据内存跟踪策略,获取list指针和used指针,将其从used指针所指的链表中删除,放置于free指针所指向的链表
相关视频推荐
庞杂的内存问题,如何理出自己的思路出来【linux内存管理】
学习地址:
https://ke.qq.com/course/417774?flowToken=1013300
需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
对方案一的分析
对照“内存池设计”一节中提出的问题,我们的方案一有以下特点:
- 程序启动后内存池并没有内存块,到程序真正进行内存申请和释放的时候才接管内存块管理;
- 该内存池对到来的申请,对申请大小并不做限制,其为每个size值创建链表进行内存管理;
- 该方案没有提供限定内存池大小的功能
结合分析,可以得出该方案应用场景如下:程序所申请的内存块大小比较固定(比如只申请/释放1024bytes或2048bytes的内存),申请和释放的频率基本保持一致(因申请多而释放少会占用过多内存,最终导致系统崩溃)。
这篇文章讲解了内存管理的基本知识,以一个简单的内存池实现例子作为敲门砖,引领大家认识内存池,下一篇为内存池进阶文章,讲解apache服务器中内存池的实现方法。
- 上一篇: Java培训:C++和Java有什么区别?
- 下一篇:已经是最后一篇了
猜你喜欢
- 2025-05-23 Java培训:C++和Java有什么区别?
- 2025-05-23 C++高精度乘法
- 2025-05-23 用C++程序解方程实例
- 2025-05-23 超详细 C/C++ 学习路线分析:学好 C/C++,走遍天下都不怕
- 2025-05-23 C++编程笔试题
- 2025-05-23 C++中的四种类型转换
- 2025-05-23 简述 C语言 有和 C++ 的基本区别,你真的懂吗?(新手面试必学)
- 2024-07-26 C++网上教程学习(c++自学)
- 2024-07-26 C/C++编程知识:运算符(七)丨三元/三目运算符知识详解
- 2024-07-26 C++各类设计模式及实现详解(c++常用设计模式详解)
- 05-23一篇文章学会c++高效内存池设计与实现(含源码)
- 05-23Java培训:C++和Java有什么区别?
- 05-23C++高精度乘法
- 05-23用C++程序解方程实例
- 05-23超详细 C/C++ 学习路线分析:学好 C/C++,走遍天下都不怕
- 05-23C++编程笔试题
- 05-23C++中的四种类型转换
- 05-23简述 C语言 有和 C++ 的基本区别,你真的懂吗?(新手面试必学)
- 最近发表
- 标签列表
-
- cmd/c (64)
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- sqlset (64)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- js数组插入 (83)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)