一种一种Linux多线程应用下内存池的设计与实现多线程应用下内存池的设计与实现
对内存池中内存块获取、分配机制、内存块大小、内存释放,以及在多线程环境下的安全处理等细节进行了研
究,保证了在多线程环境下能够快速同时采用一种基于数组的链表机制,改进内存池中内存块的查找算法,将
其时间复杂度稳定在O(1),避免了传统内存池中请求的线程数目过多时,引发的获取内存块性能下降的问题。
同时在内部设置管理线程,动态增加或删除空闲的内存块。实验结果表明,改进后的内存池与传统的内存分配
方式相比消耗更小,效率更好。
动态内存管理非常耗时,对效率影响很大,然而在实际的编程应用中,却不可避免地经常要用到堆中的内存。但是通过
Malloc函数或New等进行的内存分配存在先天缺陷: (1)利用默认的内存管理函数在堆上分配和释放内存需要花费很多时间;
(2)随着时间的推移,堆上会形成许多内存碎片,在应用程序进行内存申请操作将受到更大的影响,导致应用程序的运行越来
越慢[1-3]。
当应用程序需要对固定大小的对象经常性地申请内存时,常会采用
综上,内存池有其巨大的优势,但是原有的内存池也存在一定的缺陷。在
1 内存池制作原理以及工作流程内存池制作原理以及工作流程
本内存池基于多线程环境,需要考虑到多线程下数据的安全,以及快速获取内存块等条件。在获取内存块索引号时,采用
加锁的方式,虽然会耗费一定的时间,但是运行安全得到了保障。在程序运行之前需创建好内存池,并用一个结构体struct
mem_pool进行封装,里面包含内存池的一些私有数据。当有新线程产生时,直接像内存池申请一块已经分配好了的内存,线程
的具体内存操作都在该内存块中进行。同时,内存池结构体中隐藏一个管理线程,该线程的工作是定时检查内存池中空闲内存
块的数量是否过多或者过少。当过多时,申请释放,直到达到门槛值;当过少时,申请增加若干,以备不时之需。内存池结构
如图1所示。
对于内存池中的内存块,采用结构struct mem_block维护其数据,该结构以一个链表的形式维护实际内存区域,结构体中有
两个管理内存的区域:(1)常规大小链表区域。当需要的内存小于常规大小时,则线程的内存请求都将从该区域获得;当该区
域内存将满时,则线程可以继续申请同样大小的内存块,链接到该常规大小链表上。(2)大块内存链表区域。当线程申请的内
存超过该内存块的大小时,将在系统中申请一块大的内存链接到该大块内存链表上,这样可以对内存块进行统一管理;当线程
寿命结束时,调用reset函数将大块内存释放,同时重设常规内存链表区域,只保留最开始的一块,其余后面申请的块全部释
放,并标记内存块的使用状态为空闲,同时重设一些内部指针,指向内存块可用的起始位置[4]。
创建内存池结构,并初始化,此时在内存中存储着一份内存池的动态管理单元。当创建新线程时,该线程通过内存块查找
函数,并查询内存池结构。如有空闲内存块则直接将该内存块的索引号index送给线程,同时将该内存块的空闲标志flag设为繁
忙;如果内存池中没有可用的空闲内存块,且内存块数量未达到设置的顶峰,则可以申请add_memblock;若正在使用的内存
块超过了最大设置的内存块数量,则线程将调用Malloc函数,并自行调用Free释放该内存块。管理线程周期性地调用
get_mp_status来查看内存池状态,若空闲线程低于门槛值(最小空闲内存块数),则调度add_memblock函数创建新的内存块到池
中;若空闲内存块高于门槛值(最大空闲内存数),则调度del_memblock销毁多余的内存块。线程生命周期结束后,将内存块
的繁忙标记设置为空闲状态,并且重新初始化该内存块,将内存块重新投入到内存池中,等待其他线程重复利用。内存池的工
作流程如图2所示。
2 内存池主要技术内存池主要技术
2.1 内存池中空闲内存块的查找方式内存池中空闲内存块的查找方式