一、前言概述

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

二、结构设计

2.1 内存池结构
  1. // 内存池结构体
  2. typedef struct
  3. {
  4. int unitsize;             // 内存单元大小,即unit的大小
  5. int initnum;             // 初始内存单元的数目
  6. int grownum;           // 每次新增内存单元的数目
  7. int totalnum;            // 内存单元总数
  8. memblock_t *block;     // memblock_t链表头
  9. char *idleunit;          // 空闲内存单元链表头
  10. #if defined(__MEMPOOL_THREAD_SUPPORT__)
  11. pthread_mutex_t lock;     // 多线程加锁 —— 建议使用某种机制,消除多线程竞争加锁!
  12. #endif /*__MEMPOOL_THREAD_SUPPORT__*/
  13. }mempool_t;
  1. // 内存块结构体
  2. typdef struct
  3. {
  4. int unitnum;           // 内存块总数
  5. int idlenum;            // 空闲内存块数
  6. mempool_t *pool;      // 宿主:所属mempool_t
  7. char *lastunit;         // 结束块地址(此变量可删除)
  8. memblock_t *next;      // 下一个memblock_t
  9. }memblock_t;
  1. // 内存单元信息
  2. typedefstruct
  3. {
  4. memblock_t *block;      // 属主:内存单元所属memblock_t
  5. char *next;             // 下一块内存块地址
  6. }memunit_info_t;
2.2 总体结构

内存池的总体结构图为:

图1 总体结构图

2.3 运行机制

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

  • 1)将每一个内存单元的大小固定化,可提高内存分配效率。比如:内存单元分别为:{4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, …}(单位:byte, 大小为4byte的整数倍)。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)

内存单元可申请使用的空间大小为4的整数倍,并依次递增。因此,定位内存池的算法:(n为内存池数组下标)

  1. n = (size>>2) - 1;
  2. if(0 != (size&(4-1))) n++;

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

3. 内存碎片较小

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

缺点:

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

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

高效内存池的设计方案[c语言]的更多相关文章

  1. 极高效内存池实现 (cpu-cache)

    视频请看 : http://edu.csdn.net/course/detail/627 1.内存池的目的 提高程序的效率 减少运行时间 避免内存碎片 2.原理   要解决上述两个问题,最好的方法就是 ...

  2. C语言内存管理(内存池)

    C语言可以使用alloc从栈上动态分配内存. 内存碎片 Malloc/free或者new/delete大量使用会造成内存碎片,这种碎片形成的机理如下: 内存碎片一般是由于空闲的内存空间比要连续申请的空 ...

  3. linux下C语言实现的内存池【转】

    转自:http://blog.chinaunix.net/uid-28458801-id-4254501.html 操作系统:ubuntu10.04 前言:     在通信过程中,无法知道将会接收到的 ...

  4. Netty内存池的整体架构

    一.为什么要实现内存管理? Netty 作为底层网络通信框架,网络IO读写必定是非常频繁的操作,考虑到更高效的网络传输性能,堆外内存DirectByteBuffer必然是最合适的选择.堆外内存在 JV ...

  5. 基于C/S架构的3D对战网络游戏C++框架 _05搭建系统开发环境与Boost智能指针、内存池初步了解

    本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...

  6. 【uTenux实验】内存池管理(固定内存池和可变内存池)

    1.固定内存池管理实验 内存管理是操作系统的一个基础功能.uTenux的内存池管理函数提供了基于软件的内存池管理和内存块分配管理.uTenux的内存池有固定大小的内存池和大小可变的内存池之分,它们被看 ...

  7. 对象池与.net—从一个内存池实现说起

    本来想写篇关于System.Collections.Immutable中提供的ImmutableList里一些实现细节来着,结果一时想不起来源码在哪里--为什么会变成这样呢--第一次有了想写分析的源码 ...

  8. 定长内存池之BOOST::pool

    内存池可有效降低动态申请内存的次数,减少与内核态的交互,提升系统性能,减少内存碎片,增加内存空间使用率,避免内存泄漏的可能性,这么多的优点,没有理由不在系统中使用该技术. 内存池分类: 1.      ...

  9. Android高效内存:让图片占用尽可能少的内存

    Android高效内存:让图片占用尽可能少的内存 一.让你的图片最小化 1.1 大图小图内存使用情况对比 大图:440 * 336    小图:220 * 168 小图的高宽都是大图的1/2--> ...

随机推荐

  1. Pycharm连接远程服务器并进行代码上传+远程调试

    前提:需要有一个远程服务器,知道他的ip.port.user.password 一.连接远程服务器 进入配置页面 Pycharm菜单栏,如下图所示,依次点击 Tools -> Deploymen ...

  2. 【SpringBoot】SpringBoot快速入门(一)

    SpringBoot介绍 Spring Boot使创建可运行的独立,生产级基于Spring的应用程序变得容易.对Spring平台和第三方库整合,这样就可以以最小的麻烦开始使用.大多数Spring Bo ...

  3. 接口测试01- Jmeter-线程进程-环境变量

    1.1 概念 JMeter 是 Apache 组织使用 Java 开发的一款测试工具 ,它最初被设计用于Web应用测试,但后来扩展到其他测试领域. 它可以用于测试静态和动态资源,例如静态文件.Java ...

  4. 【优化技巧】指数移动平均EMA的原理

    前言 在深度学习中,经常会使用EMA(exponential moving average)方法对模型的参数做平滑或者平均,以求提高测试指标,增加模型鲁棒性. 参考 1. [优化技巧]指数移动平均(E ...

  5. Java设置文件权限

    今天遇到一个问题: java写的API,ppt转图片生成的目录及文件 在使用php调用API完成后,再使用php进行删除时,遇到了删除失败的问题(php删除的部分  查看) 部署的环境是Ubuntu ...

  6. Spring boot后台搭建二集成Shiro添加Remember Me

    上一片文章实现了用户验证  查看 当用户成功登录后,关闭浏览器,重新打开浏览器访问http://localhost:8080,页面会跳转到登录页,因为浏览器的关闭后之前的登录已失效 Shiro提供了R ...

  7. 【视频开发】OpenCV中Mat,图像二维指针和CxImage类的转换

    在做图像处理中,常用的函数接口有OpenCV中的Mat图像类,有时候需要直接用二维指针开辟内存直接存储图像数据,有时候需要用到CxImage类存储图像.本文主要是总结下这三类存储方式之间的图像数据的转 ...

  8. dubbo问题集合

    背景:用于整理在开发过程中遇到的相关问题. Dubbo Server的时候出现“Will not attempt to authenticate using SASL (unknown error)” ...

  9. win10锁住鼠标和键盘操作。

    以前做的一个winform自动更新程序没考虑到程序在更新过程中禁止操作被更新程序.现在加上了更新过程中锁住鼠标和大部分键盘. 碰到问题:用系统api -- BlockInput(true)锁住屏幕无效 ...

  10. [08]Go设计模式:代理模式(ProxyPattern)

    目录 proxy 一.简介 二.代码 三.参考资料 proxy 一.简介 代理设计模式是一种结构设计模式.这种模式建议为控制和访问主要对象提供额外的间接层. 在这种模式下,将创建一个新的代理类,该类实 ...