• 内存池的实现(一)


    引言

    C/C++下内存管理是让几乎每一个程序员头疼的问题,分配足够的内存、追踪内存的分配、在不需要的时候释放内存——这个任务相当复杂。而直接使用系统调用malloc/free、new/delete进行内存分配和释放,有以下弊端:

    1. 调用malloc/new,系统需要根据“最先匹配”、“最优匹配”或其他算法在内存空闲块表中查找一块空闲内存,调用free/delete,系统可能需要合并空闲内存块,这些会产生额外开销
    2. 频繁使用时会产生大量内存碎片,从而降低程序运行效率
    3. 容易造成内存泄漏

     

    内存池(memory pool)是代替直接调用malloc/freenew/delete进行内存管理的常用方法,当我们申请内存空间时,首先到我们的内存池中查找合适的内存块,而不是直接向操作系统申请,优势在于:

    1. 比malloc/free进行内存申请/释放的方式快
    2. 不会产生或很少产生堆碎片
    3. 可避免内存泄漏

     

    内存池设计

    看到内存池好处这么多,是不是恨不能马上抛弃malloc/free,投奔内存池的怀抱呢?且慢,在我们自己动手实现内存池之前还需要明确以下几个问题:

    1. 内存池的空间如何获得?是程序启动时分配一大块空间还是程序运行中按需求分配?
    2. 内存池对到来的内存申请,有没有大小的限制?如果有,最小可申请的内存块为多大,最大的呢?
    3. 如何合理设计内存块结构,方便我们进行内存的申请、追踪和释放呢?
    4. 内存池占用越多空间,相对应其他程序能使用的内存就越少,是否要设定内存池空间的上限?设定为多少合适呢?

    带着以上问题,我们来看以下一种内存池设计方案。

     

    内存池实现方案一

    这里下载该内存池实现的源码。

    首先给出该方案的整体架构,如下:

    内存池实现架构

    图1.内存池架构图

    结构中主要包含blocklist 和pool这三个结构体,block结构包含指向实际内存空间的指针,前向和后向指针让block能够组成双向链表;list结构中free指针指向空闲 内存块组成的链表,used指针指向程序使用中的内存块组成的链表,size值为内存块的大小,list之间组成单向链表;pool结构记录list链表的头和尾。

     

    内存跟踪策略

    该方案中,在进行内存分配时,将多申请12个字节,即实际申请的内存大小为所需内存大小+12。在多申请的12个字节中,分别存放对应的list指针(4字节)、used指针(4字节)和校验码(4字节)。通过这样设定,我们很容易得到该块内存所在的list和block,校验码起到粗略检查是否出错的作用。该结构图示如下:

    结点头12字节说明

    图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指针所指向的链表

     

    对方案一的分析

    对照“内存池设计”一节中提出的问题,我们的方案一有以下特点:

    1. 程序启动后内存池并没有内存块,到程序真正进行内存申请和释放的时候才接管内存块管理;
    2. 该内存池对到来的申请,对申请大小并不做限制,其为每个size值创建链表进行内存管理;
    3. 该方案没有提供限定内存池大小的功能

     

    结合分析,可以得出该方案应用场景如下:程序所申请的内存块大小比较固定(比如只申请/释放1024bytes或2048bytes的内存),申请和释放的频率基本保持一致(因申请多而释放少会占用过多内存,最终导致系统崩溃)。

     

    这篇文章讲解了内存管理的基本知识,以一个简单的内存池实现例子作为敲门砖,引领大家认识内存池,下一篇为内存池进阶文章,讲解apache服务器中内存池的实现方法。

     

    Reference: 《内存池设计研究与应用》by freeeyes

  • 相关阅读:
    Open source cryptocurrency exchange
    Salted Password Hashing
    95. Unique Binary Search Trees II
    714. Best Time to Buy and Sell Stock with Transaction Fee
    680. Valid Palindrome II
    Java compiler level does not match the version of the installed Java project facet.
    eclipse自动编译
    Exception in thread "main" java.lang.StackOverflowError(栈溢出)
    博客背景美化——动态雪花飘落
    java九九乘法表
  • 原文地址:https://www.cnblogs.com/bangerlee/p/2161421.html
Copyright © 2020-2023  润新知