高效内存池的设计方案 C语言

本文介绍了作者邹祁峰提出的一种内存池设计方案,旨在提高内存分配和释放的效率,时间复杂度为O(1)。内存池通过固定大小的内存单元和链表结构管理,减少了内存碎片并能动态扩展。文章详细描述了内存池的结构设计、运行机制及优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

               


1 引言

    本人在转发的博文《内存池的设计和实现》中,详细阐述了系统默认内存分配函数malloc/free的缺点,以及进行内存池设计的原因,在此不再赘述。通过对Nginx内存池以及《内存池的设计和实现》的分析后,现提出一种性能更优(申请/释放内存时间复杂度为O(1))的内存池的设计方案。如有不妥之处,欢迎指正!如有其他的内存池的设计方案,欢迎共同分享和探讨。注意:LINUX内核、NGINX、MEMCACHE使用SLAB机制内存分配方案; 还有Jemalloc内存池与系统内存申请释放相兼容】


2 结构设计

2.1 内存池结构

/* 内存池结构体 */typedef struct{    int unitsize;             /* 内存单元大小,即unit的大小 */    int initnum;              /* 初始内存单元的数目 */    int grownum;              /* 每次新增内存单元的数目 */    int totalnum;             /* 内存单元总数 */    memblock_t *block;        /* memblock_t链表头 */    char *idleunit;           /* 空闲内存单元链表头 */#if defined(__MEMPOOL_LOCK__)    spinlock_t lock;          /* 自旋锁:使用自旋锁有效避免CPU切换的开销 */#endif /*__MEMPOOL_LOCK__*/}mempool_t;

代码1 内存池结构

/* 内存块结构体 */typdef struct{    int unitnum;              /* 内存块总数 */    int idlenum;              /* 空闲内存块数 */    mempool_t *pool;          /* 所属池:所属mempool_t */    char *lastunit;           /* 结束块地址(此变量可删除) */    memblock_t *next;         /* 下一个memblock_t */}memblock_t;

代码2 内存块结构

/* 内存单元信息 */typedefstruct{    memblock_t *block;        /* 所属块:内存单元所属memblock_t */    char *next;               /* 下一块内存块地址 */}memunit_info_t;

代码3 内存单元信息

2.2 总体结构

内存池的总体结构图为:

图1 总体结构图

2.3 运行机制

此内存池的运行机制如下:

  • 1)将每一个内存单元的大小固定化,可提高内存分配效率。比如:内存单元分别为:{8, 16, 32, 64, 128, 256, 512, 1024, …}(单位:byte)。Mempool_t之间是通过数组形式组织的,其大体结构如下:(注:为了明确Mempool_t之间的关系,未标出其他变量之间的关系)

图2 Mempool_t数组

[注:为提高效率,通过数组存储Mempool_t,在申请内存空间时,可通过偏移量快速定位使用哪个大小的内存池]

  • 2)内存池实际可供分配的内存单元是在Memblock_t中,当所有Memblock_t中的内存单元被使用完后,则需申请开辟一个新的Memblock_t,并加入到Memblock_t链表之中。Memblock_t的组织方式为:(注:为了明确Memblock_t之间的关系,未标出其他变量之间的关系)

图3 Memblock_t链表

  • 3)使用链表组织空闲内存单元,可大大提高内存分配/释放时的效率(时间复杂度为O(1))。Mempool_t中的idleunit是空闲内存单元的链表头。空闲内存单元的组织形式如下:(注:为明确空闲内存单元之间的关系,未标明其他变量之间的关系)

图4 空闲内存单元链表

[说明:Memblock_t中的用红色数字标记的内存单元代表已被分配内存单元,

用绿色数字标记的内存单元代表空闲内存单元]

  • 4)当申请内存时,将idleunit指向的内存单元踢出空闲内存单元链表,并idleunit指向内存单元的后继,再返回该内存单元的地址。以图4为例,申请内存块后,空闲内存单元链表如图所示:(注:请对比与图4之间的变化)

图5 内存申请图

[注:当申请的内存空间size比所有的内存单元都大时,

则通过malloc()向OS申请size+sizeof(memunit_info_t)的内存空间]

  • 5)当释放内存单元unitn时,将unitn的后继改为idleunit的指向,同时将idleunit指向要释放的内存单元unitn。以图4为例,释红色数字标记的内存单元3后,空闲内存单元链表如图所示:(注:请对比与图4之间的变化)

图6 内存释放图

  • 6)内存单元是通过链表形式进行组织管理的,因此,每个内存单元有额外的空间用来存放组织链表的信息。将图4进一步展开:(注:请结合图4一起看)

图7 内存单元内部结构

说明:

  -> 1. 每个内存单元的内部结构:memunit_info_t结构+unitsize大小的空间。每个内存单元的大小为:sizeof(memunit_info_t)+unitsize;

  -> 2. idleunit指向的是内存单元的data;空闲内存单元的next指向的是后继内存单元的data,无后继则为NULL;已分配的内存单元的next始终为NULL。

  -> 3. 内存单元的block指向宿主Memblock_t,这可快速的确定对当前内存单元属于哪个Memblock_t,再通过Memblock_t中的pool,可快速获知属于哪个Mempool_t。

  -> 4. 在分配内存时,返回给用户的是data的地址,而不是内存单元的地址。

  • 7)在释放内存单元时,为使被释放内存单元加入空闲内存单元链表,可通过内存单元的block获知所属Memblock_t,再通过pool获知所属Mempool_t,因此,便可知空闲内存单元链表头idleunit,此时便可将被释放的内存单元加入空闲链表。

图8 所属Mempool_t

2.4 优缺点

通过对以上几点的分析,可知此内存池有以下优缺点:

优点:

  1. 定位内存池的时间复杂度为O(1)

    内存单元可申请使用的空间依次为8、16、32、64、128、256、512、1024 ..., 因此,定位内存池的算法:(n为内存池数组下标)

if(size > 8){    shift=1;    for(s=size-1; s>=1; ++shift) { NULL; } /* 计算位移 */    n = shift - 3; /* 计算角标 */}else{    n = 0;}

代码4 计算角标


  2. 申请和释放内存的时间复杂度为O(1)

  3. 有效减少内存碎片

  4. 较小的互斥粒度:申请空间时,每次只锁住对应的mempool_t的内存池,依然可以申请其他size的内存池空间[注:如果再加入为每个线程分配一个内存池对象的机制,则可达到零互斥零竞争。这样的话,可不使用互斥机制,同时能够进一步提高性能]

  5. 内存空间可动态扩展。

缺点:

  1. 内存单元的实际大小要比unitsize多sizeof(memunit_info_t)个字节

  2. 空闲内存单元链表中的内存单元是乱序串联的,因此会造成即使空闲内存单元个数超过单个Memblock_t内存单元总数时,操作系统可能依然无法释放任何一个Memblock_t对象。

 

           
如何顺利通过电磁兼容试验——认证检测中常见的电磁兼容问题与对策 1 .概述 1.1 什么时候需要电磁兼容整改及对策 对一个电子、电气产品来说,在设计阶段就应该考虑其电磁兼容性,这样可以将产品在生产阶段出现电磁兼容问题的可能性减少到一个较低的程度。但其是否满足要求,最终要通过电磁兼容测试检验其电磁兼容标准的符合性。由于电磁兼容的复杂性,即使对一个电磁兼容设计问题考虑得比较周全得产品,在设计制造过程中,难免出现一些电磁干扰的因素,造成最终电磁兼容测试不合格。在电磁兼容测试中,这种情况还是比较常见的。当然,对产品定型前的电磁兼容测试不合格的问题,我们完全可以遵循正常的电磁兼容设计思路,按照电磁兼容设计规范法和系统法,针对产品存在的电磁兼容问题重新进行设计。从源头上解决存在的电磁兼容隐患。这属于电磁兼容设计范畴。而目前国内电子、电气产品比较普遍存在的情况是:产品在进行电磁兼容型式试验时,产品设计已经定型,产品外壳已经开模,PCB 板已经设计生产,部件板卡已经加工,甚至产品已经生产出来等着出货放行。对此类产品存在的电磁兼容问题,只能采取“出现什么问题,解决什么问题”的问题解决法,以对产品的最小改动使其达到电磁兼容要求。这就属于电磁兼容整改对策的范畴,这是我们这次课程需要探讨的问题。 共四章,后续略
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值