简单内存池设计与实现

使用Java进行开发的时候,不用过多地考虑内存的问题。而C/C++就不得不考虑了,比如每一次的内存申请(malloc),都不能忘了释放(free),不然会出现内存泄漏等等诸多问题。在网络服务程序里,每来一个请求或者链接,都会开辟一小块内存来对其进行相应的处理,如果每次都向操作系统申请内存的话,会使得网络服务程序的性能大打折扣。在这种情况里,是非常有必要使用内存池的。近期在做一个消息转发机制的轮子,其中需要使用内存池,所以参照了Apache的内存池进行简单的设计(修剪<( ̄︶ ̄)>)和在Linux下的实现。

内存池

内存池 (Memory Pool),又被称为固定大小区块规划 (fixed-size-blocks allocation),允许程序员以类似 C语言 的 malloc 或是 C++ 的 new 运算对象进行动态的内存规划。对于其它动态内存规划的实践来说,因为会变动内存区块大小导致的碎片问题,导致在实时系统上受限于性能因此,根本无法使用。内存池提供了一个更有效率的解决方案:预先规划一定数量的内存区块,使得整个程序可以在运行期规划 (allocate)、使用 (access)、归还 (free) 内存区块 —— [ 维基百科 ]

设计

主要参照Apache内存池进行设计,说到底就是Apache内存池的简化版。保留Apache的内存池节点分配器,并且对其进行修剪。只设计内存池的核心操作,如内存池创建、内存池销毁、向内存池申请内存和归还内存。并且还为其设计(仿照Apache_(:зゝ∠)_)了内存池空间大小动态增长的特点,即内存池并不会一开始向操作系统申请一大块内存,初始状态时,内存池的大小为一个比较小的值,其随着用户内存的申请而增长。

内存池节点

内存池节点是内存池分配内存块的最小单元,其有5个区域,分别是:链表后继指针域、链表前驱指针域、index域、使用标志和用户使用内存块,如下图所示。其中,用户使用内存块的前四个区域为内存池节点头。

内存池节点结构图

链表后继指针域(next)链表前驱指针域(pre) 使内存池节点可插入到使用链表或index对应的空闲链表。
index域 即可作为分配器空闲数组free[]的下标来索引空闲链表,也可描述内存池节点的大小。
使用标志(use_flg) 当为0时表明内存池节点空闲,为1时表明正在被用户使用。
用户使用内存块 是用户真正使用的内存。

啊,,,小伙伴们看到这里,是不是对index处于懵逼状态╮(╯▽╰)╭?本来想在这里就对其进行详细介绍,但在后面的内存分配器的介绍中会涉及到,所以就放在后面一块介绍吧,在此先作一个印象。

内存分配器

Apache中的大部分的内存的分配都是由内存分配器allocator完成的。在这,Apache的内存分配器被我作了很大修剪,并且用内存分配器来代表一个内存池,所有对内存池的操作都需要通过其来进行。内存分配器的数据结构图及其与内存池节点的关系如下图所示:

内存分配器数据结构图及其与内存池节点的关系

是的,本次设计的内存池所有数据结构都被上图给画完了,就是这么简单<( ̄ˇ ̄)/!通过上图,小伙伴们是否对Index窥见一斑?index有两个身份:第一,它可以描述内存池节点的大小size = (index + 1)*ALIGN,其中ALGIN为用户可以设置的对齐大小;第二,它可以作为内存分配器中free数组的索引,当用户需要向内存池申请内存时,计算得出合适的索引,并且通过该索引可以找到对应大小内存池节点的空闲链表,进而取出合适大小的内存池节点。

下面对内存分配器的各区域进行介绍:
max_free_index 描述内存池的最大空闲内存大小。因为本次设计的内存池的内存空间是动态增长的,随着用户内存的申请,内存池节点会增多,相对的,空闲内存池节点也会增多,如果没有对空闲内存池节点进行限制,内存池就会占用很大的空闲内存,造成系统内存资源的很大浪费。
current_free_index 描述目前内存池空闲内存大小(不包括使用中的内存大小)。
use_head 正在使用的内存池节点链表头。该链表是双向链表,之所以设计成双向链表,是因为只需提供要被移出链表的节点指针即可对节点进行移除。也即是当用户需要释放内存时,计算出用户使用内存块对应的内存池节点头的指针即可把内存池节点移出使用链表,进而插入到相对应的内存池节点空闲链表。
*free[MAX_INDEX + 1] 空闲链表数组。这是一个非常重要的数组,其每个元素保存着相应大小的空闲内存池节点链表首节点指针。除了free[MAX_INDEX]元素,每个元素free[index]保存着大小为size = (index + 1)*ALIGN的内存池节点链表的首节点指针。而free[MAX_INDEX]元素保存着大小为size > (MAX_INDEX + 1)*ALIGN的内存池节点链表首节点指针。用户每一次的内存申请,都会访问一次free[]数组,找到对应内存池节点链表,进而取出合适的内存池节点。用户的每一次释放,如果释放之后内存池空闲空间大小不超过限制,也会把要释放的内存池节点插入到free[]数组里某个对应大小的空闲内存池节点链表中。
mutex 锁,用来互斥访问内存分配器。因为内存池有大量的链表的插入和移除操作,在多线程中,如果不进行互斥访问,内存池将会变得非常不安全。

内存申请

当用户向内存池申请内存时,内存池的主要工作就是:根据用户需要申请内存的大小选择一个合适的空闲内存池节点,把该节点的用户使用内存块的首地址返回给用户。选择空闲内存池节点时,会遇到两种情况:情况一,对应的Index在区间[0,MAX_INDEX-1]内;情况二,用户所申请内存大小对应的Index大于等于MAX_INDEX。以下介绍在各个情况里,内存池所做的工作。

情况一

在这种情况下,内存池可以非常快地找到合适的内存池节点。内存池直接把Index作为free数组的下标,索引到Index对应的内存池节点链表的首节点指针,然后从链表取出首节点,把其插入到使用链表(链表头为use_head)里,最后把该节点的用户使用内存块的首地址返回给用户。如果free[]数组中Index对应的元素是空的,即内存池没有存在Index对应的空闲内存池节点,则向操作系统申请内存,生成空闲内存池节点,把其插入到使用链表后,返回用户使用内存块的首地址给用户(内存池空间大小动态增长)。显然,在这种情况里,如果内存池有对应大小的空闲内存池节点,用户向内存池申请内存的时间复杂度为O(1)。流程图如下:

Created with Raphaël 2.1.0 index∈[0,MAX_INDEX-1] free[index] !=NULL? 取出free[index]对应空闲链表的首节点 把内存池节点插入到使用链表 返回用户使用内存块首地址 向操作系统申请index对应大小的内存 生成空闲内存池节点 yes no

情况二

Index大于等于MAX_INDEX的内存池节点是比较大的了,可以认为用户申请其的频率很小,故不会对其单独组织成一个链表。所以把所有Index大于等于MAX_INDEX的内存池节点从小到大连接成一个链表,挂在free[MAX_INDEX]下。用户向内存池申请的内存大小大于等于MAX_INDEX对应的大小时,将会遍历free[MAX_INDEX]对应的链表,如果找到合适的空闲内存池节点则取出,如果找不到,则向操作系统申请相应大小内存并生成空闲内存池节点,最后将节点插入到使用链表,把用户使用内存块的首地址返回给用户。显然时间复杂度与free[MAX_INDEX]对应空闲内存池节点链表长度有关,为O(n)。流程图如下:

Created with Raphaël 2.1.0 index>=MAX_INDEX 遍历free[MAX_INDEX]对应的空闲链表 是否存在于链表? 从空闲链表取出index节点 把内存池节点插入到使用链表 返回用户使用内存块首地址 向操作系统申请index对应大小的内存 生成空闲内存池节点 yes no

内存释放

内存释放就是用户把向内存池申请到的内存归还给内存池,把申请到的内存块首地址交给内存池即可释放。实际上,在内存释放的过程中,内存池所做的工作仅仅是通过用户内存块首地址减去一个偏移量(内存池节点头大小)得出内存池节点首地址,然后把该节点移出使用链表。如果释放后内存池总空闲空间大小没有超过最大值(max_free_index),则把移出的内存池节点插入到free[]数组中对应Index的空闲链表里,否则把节点所占的内存空间还给操作系统。逻辑很简单,作者很懒,所以就不画流程图了。当归还的内存对应的index在区间[0,MAX_INDEX-1]上,并且归还后内存池总空闲空间没有超过最大值,内存释放的时间复杂度是O(1),可见非常快。

小结

在此,已经把简单内存池的数据结构和核心操作的设计介绍得差不多了,还差内存池的创建和销毁,还有多线程安全,因为篇幅问题,加上作者很懒,所以就不作多介绍了。这个内存池的数据结构是参照Apache设计的,其实还有各种各样结构的内存池,不仅限于如此。该内存池的优点和性能,在上面稍微作了一些分析,在一定情况下申请和释放特别快。不足之处也有许多,下面列举几个:

  1. 如果用户申请的内存大小变化特别大,空闲链表命中率(free[]数组存在对应Index的空闲链表的概率)就会特别低,导致几乎每次向内存池申请内存时,实际上是向操作系统申请内存,内存池作用不大。
  2. 不适合申请超大空间内存。
  3. 如果用户在某个瞬间申请大量Index=1的内存空间,而后在某个瞬间全部归还给内存池之后一直不使用,则这些空闲内存池节点就成了“僵尸节点”,很显然浪费了系统资源。
  4. 等等。。。

对于第2点,可以把free[MAX_INDEX]对应的链表改成小根堆而得以改进。使用小根堆之后,内存申请和释放的时间复杂度都是O(logn)。对于第3点,可以给内存池节点加上时间戳,再设计一个定时器,定时清除“僵尸节点。”

实现

来不及解释了,快上车!有什么话车上讲(注释)!
文件mmpool.h

/*嗯,这是参照Apache设计的一个内存池,或者说是Apache内存池的一个简易版,如果写完
 * 了之后觉得挺好用的,那我就继续添加树型结构。也挺担心到时就挺迟的了。
 */


#include "semaphore.h"   //没有移植的打算


#define MMPL_MAX_INDEX 10
/*下面是内存池的默认配置,内存池最大内存空间是200M*/
#define MMPL_MAX_FREE_INDEX_DEFAULT (1<<10)*100
#define MMPL_BOUNDARY_DEFAULT 2048
#define MMPL_BOUNDARY_INDEX 12
/*匹配最接近size的boundary的整数倍的整数,boundary必须为2次幂*/
#define MMPL_ALIGN(size,boundary) \
    (((size) + ((boundary) - 1)) & ~((boundary) - 1))
#define MMPL_ALIGN_DEFAULT(size) MMPL_ALIGN((size),MMPL_BOUNDARY_DEFAULT)

/*如同Apache的内存节点*/
struct mm_node{
    int use_flg; //使用标志,0表示没有在使用,1表示正在被使用
    struct mm_node *next;  //下一个节点
    struct mm_node *pre;  //上一个节点
    unsigned int index;  //既可以表示节点内存的大小,也可以作为free数组的下标

};


struct mm_pool_s{
    /*内存池最大内存空间大小,防止向操作系统申请过多内存空间致使操作系统崩溃*/
    unsigned int max_free_index; //最大空闲内存
    unsigned int current_free_index;   //当前内存池空闲的内存
    struct mm_node use_head;  //正在使用的内存节点的链表头
    struct mm_node *free[MMPL_MAX_INDEX + 1];
    sem_t mutex;  //锁,用来互斥访问内存池
};

int mmpl_create(struct mm_pool_s **new_mmpl);
int mmpl_destroy(struct mm_pool_s *p_mmpl);
void* mmpl_getmem(struct mm_pool_s *p_mmpl,unsigned int size);
int mmpl_rlsmem(struct mm_pool_s *p_mmpl,void *rls_mmaddr);
int mmpl_list_remove(struct mm_node *p_rm_node);

文件mmpool.c

#include "mmpool.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
#include "unistd.h"



/* 函数名: int mmpl_create(struct mm_pool_s *new_mmpl)
 * 功能: 创建新的内存池
 * 参数: struct mm_pool_s *new_mmpl,内存池结构体指针
 * 返回值: -1,
 *          1,
 */
int mmpl_create(struct mm_pool_s **new_mmpl){
    *new_mmpl = (struct mm_pool_s *)malloc(sizeof(struct mm_pool_s));
    memset((void*)*new_mmpl,0,sizeof(struct mm_pool_s));
    (*new_mmpl)->max_free_index = MMPL_MAX_FREE_INDEX_DEFAULT;
    (*new_mmpl)->current_free_index = 0;
    (*new_mmpl)->use_head.next = &((*new_mmpl)->use_head);
    (*new_mmpl)->use_head.pre = &((*new_mmpl)->use_head);
    sem_init(&(*new_mmpl)->mutex,0,1);  //初始化锁
    return 1;
}



/* 函数名: int mmpl_destroy(struct mm_pool_s *p_mmpl)
 * 功能: 销毁内存池,把内存空间归还给操作系统
 * 参数: struct mm_pool_s *p_mmpl,需要销毁的内存池的结构体
 * 返回值: -1,
 *          1,
 */
int mmpl_destroy(struct mm_pool_s *p_mmpl){
    int i;
    struct mm_node *p_mm_n,*p_free_h;

    if(p_mmpl == NULL){
        return -1;
    }
    sem_wait(&p_mmpl->mutex); //互斥访问内存池
    /*归还正在使用的内存空间给操作系统*/
    while(p_mmpl->use_head.next != &p_mmpl->use_head){
        p_mm_n = p_mmpl->use_head.next;
        mmpl_list_remove(p_mm_n);
        free(p_mm_n);
    }
    /*归还内存池中空闲内存空间给操作系统*/
    for(i = 1;i < MMPL_MAX_INDEX + 1;i++){
        p_free_h = p_mmpl->free[i];
        if(p_free_h == NULL)continue;
        while(p_free_h->next != p_free_h){
            p_mm_n = p_free_h->next;
            mmpl_list_remove(p_mm_n);
            free(p_mm_n);
        }
        free(p_free_h);
    }
    sem_post(&p_mmpl->mutex);
    sem_destroy(&p_mmpl->mutex);
    free(p_mmpl);
    return 1;
}

/* 函数名: int mmpl_list_insert(struct mm_node *pre_p_n,struct mm_node *p_insert_n)
 * 功能: 把内存节点插入到链表
 * 参数: struct mm_node *pre_p_mm_n,需要插入位置的前驱节点指针
 *       struct mm_node *p_insert_mm_n,需要插入的节点的指针
 * 返回值: -1,
 *          1,
 */
int mmpl_list_insert(struct mm_node *p_pre_n,struct mm_node *p_insert_n){
    if(p_pre_n == NULL || p_insert_n == NULL){
        return -1;
    }
    p_insert_n->next = p_pre_n->next;
    p_insert_n->pre = p_pre_n;
    p_pre_n->next->pre = p_insert_n;
    p_pre_n->next = p_insert_n;
    return 1;
}

/* 函数名: int mmpl_list_remove(struct mm_node *p_rm_node)
 * 功能: 从节点所在的链表中移除该节点
 * 参数: struct mm_node *p_rm_node,需要移除的节点
 * 返回值: -1,
 *          1,
 */
int mmpl_list_remove(struct mm_node *p_rm_node){
    if(p_rm_node->next == p_rm_node){  //如果链表就只有该节点,则无法删除
        return -1;
    }
    p_rm_node->next->pre = p_rm_node->pre;
    p_rm_node->pre->next = p_rm_node->next;
    return 1;
}


/* 函数名: void* mmpl_getmem(struct mm_pool_s *p_mmpl,unsigned int size)
 * 功能: 从指定的内存池里获取到内存空间
 * 参数: struct mm_pool_s *p_mmpl,内存池结构体指针
 *       int size,需要申请空间的大小
 * 返回值: NULL,获取失败
 *         !=NULL,获取到的内存地址
 */
void* mmpl_getmem(struct mm_pool_s *p_mmpl,unsigned int size){
    unsigned int align_size;
    unsigned int index;
    struct mm_node *p_mm_n;

    align_size = MMPL_ALIGN_DEFAULT(size);  //默认2K对齐
    index = align_size/MMPL_BOUNDARY_DEFAULT;
    if(index > MMPL_MAX_INDEX){  //如果超过MMMPL_MAX_INDEX则向操作系统要
        index = 0;
    }
    sem_wait(&p_mmpl->mutex);  //互斥操作内存池
    if(((p_mm_n = p_mmpl->free[index]) == NULL) || index == 0){  
        /*如果free数组中没有相应的内存节点,则向操作系统申请*/
        p_mm_n = (struct mm_node *)malloc(align_size + sizeof(struct mm_node));
        if(p_mm_n == NULL){//向操作系统申请内存失败
            sem_post(&p_mmpl->mutex);
            return NULL;
        }
        p_mm_n->pre=p_mm_n->next = p_mm_n;
        p_mm_n->index = index;
    }else if(p_mm_n->next == p_mm_n){
        /*如果free[index](规则链表)链表只有一个节点*/
        p_mmpl->free[index] = NULL;
        p_mmpl->current_free_index -= index; //空闲内存空间减少
    }else{
        p_mmpl->free[index] = p_mm_n->next;
        mmpl_list_remove(p_mm_n);
        p_mmpl->current_free_index -= index; //空闲内存空间减少
    }
    p_mm_n->use_flg = 1; //表示正在被使用
    mmpl_list_insert(&p_mmpl->use_head,p_mm_n); //插入到使用链表中
    sem_post(&p_mmpl->mutex);
    return (void *)p_mm_n + sizeof(struct mm_node);
}


/* 函数名: int mmpl_rlsmem(struct mm_pool_s *p_mmpl,void *rls_mmaddr)
 * 功能: 把从内存池申请到的内存空间还给内存池,如果还给内存池之后空闲的内存空间超
 *       过了内存池所设置的最大空闲内存,则把将要归还给内存池的内存空间直接还给操
 *       作系统。
 * 参数: strcut mm_pool_s *p_mmpl,内存池
 *       void *rls_mmaddr,需要释放的内存空间的首地址
 * 返回值:
 */
int mmpl_rlsmem(struct mm_pool_s *p_mmpl,void *rls_mmaddr){
    struct mm_node *p_mm_n;  
    unsigned int index;

    p_mm_n = rls_mmaddr - sizeof(struct mm_node);//获得内存节点的首地址
    if(p_mm_n->use_flg == 0){ //如果本来就是空闲的,则放弃回收
        return -1;
    }
    index = p_mm_n->index;
    sem_wait(&p_mmpl->mutex);  //互斥操作内存池
    mmpl_list_remove(p_mm_n);  //从使用链表中移除
    p_mm_n->use_flg = 0;
    if(index == 0){  //如果超过了最大index,则直接还给操作系统
        free(p_mm_n);
        sem_post(&p_mmpl->mutex);
        return 1;
    }
    if(p_mmpl->current_free_index + index > p_mmpl->max_free_index){
        /*如果归还之后内存池的空闲空间超过了最大空闲空间大小则直接归还给操作系统*/
        free(p_mm_n);
        sem_post(&p_mmpl->mutex);  //互斥操作内存池
        return 1;
    }
    if(p_mmpl->free[index] == NULL){
        p_mm_n->pre=p_mm_n->next = p_mm_n;
        p_mmpl->free[index] = p_mm_n;
    }else{
        if(mmpl_list_insert(p_mmpl->free[index],p_mm_n) == -1){
            printf("error\n");
            return -1;
        }
    }
    p_mmpl->current_free_index += index;
    sem_post(&p_mmpl->mutex);  //互斥操作内存池
    return 1;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一个比较复杂的任务,需要考虑很多细节。一般来说,内存分配与回收程序需要实现以下几个功能: 1. 初始化内存池:程序需要在开始时分配一定大小的内存池,用于存储分配出去的内存块。 2. 分配内存:当程序需要分配内存时,需要从内存池中找到一个足够大的内存块,并将其标记为已分配状态。 3. 回收内存:当程序不再需要某个内存块时,需要将其标记为未分配状态,并将其加入到可用内存块列表中。 4. 内存池扩容:当内存池中的可用内存块不足时,程序需要重新分配更大的内存池,并将原有的内存块复制到新的内存池中。 5. 内存池缩容:当内存池中的可用内存块过多时,程序需要将多余的内存块释放掉,并将内存池缩小到合适的大小。 以上是内存分配与回收程序的基本功能,实现起来需要考虑很多细节,比如内存对齐、内存碎片整理等。如果需要实现一个简单内存分配与回收程序,可以先从基本功能入手,逐步完善程序。 ### 回答2: 内存分配与回收程序是计算机操作系统设计中非常重要的一部分。程序需要在运行时动态地分配和回收内存,以便满足程序的运行需求。为了实现一个简单内存分配与回收程序,我们需要先了解一些基本概念。 内存分配可以通过申请系统空间或者从已有的空闲内存中分配得到。一种简单内存分配方式是使用固定大小的内存池内存池按照相等的大小被划分为多个块,一个块的大小就是系统页面大小。分配器可以通过一个指针来追踪下一个可用块的位置。当程序请求内存时,分配器可以将下一个可用块的指针返回给程序。如果块已经被分配了,分配器会从下一个可用块的位置重新开始寻找。当程序释放内存时,这块内存变成了可用的,分配器会把它加入内存池的空闲块列表中。 下面是一个简单内存分配器的伪代码: ```python class MemoryPool: def __init__(self, block_size, block_count): self.block_size = block_size self.block_count = block_count self.memory = [0] * block_size * block_count self.next_free_block = 0 self.free_list = [i for i in range(block_count)] def allocate(self): if not self.free_list: return None free_block_index = self.free_list.pop() block_address = free_block_index * self.block_size self.next_free_block = self.free_list[0] * self.block_size return self.memory[block_address:block_address+self.block_size] def deallocate(self, block): block_address = block - self.memory[0] block_index = block_address / self.block_size self.free_list.append(block_index) self.free_list.sort() ``` 内存分配器的实现要考虑到效率和内存使用的平衡问题。为了提高效率,我们需要尽可能地重复利用已有的内存,对于小块内存,可以采用缓存分配等策略。对于大块内存,可以使用连续的内存页进行分配。 除了分配内存外,内存回收同样重要。内存回收目的是将程序不再需要的内存块返回给内存池实现内存的重用。内存回收可以使用引用计数、垃圾回收等多种方式。在程序中,需要注意合理地发起内存回收对于系统内存资源的利用。 总之,内存分配与回收程序设计中,需要考虑到多方面的因素,包括内存空间大小、效率、内存碎片等。同时,需要根据程序实际需求,采用不同的内存位置和策略。最后,需要定期检测内存使用情况,合理地回收不再需要的内存,为程序运行提供高效的内存服务。 ### 回答3: 内存分配与回收是计算机体系结构中至关重要的一个组件。每个程序都需要内存空间来存储数据和执行指令。内存管理器是操作系统的核心之一,负责将物理内存分配给进程使用,并在进程终止或释放内存时将其回收。 要设计实现一个简单内存分配与回收程序,需要先了解计算机内存的基本原理。内存通常被组织成一系列连续的单元,每个单元有唯一的地址。程序通过地址来访问内存中的数据。内存分配器需要跟踪哪些内存单元可用,哪些已被分配。当程序请求内存时,内存分配器应该为其分配足够的连续内存单元,并返回起始地址。当程序不再需要这些内存空间时,内存分配器应该将它们标记为空闲状态,以便下次分配使用。 设计内存分配器时,需要考虑以下问题: 1. 如何跟踪哪些内存单元可用,哪些已被分配?我们可以使用链表数据结构来跟踪内存单元的状态。每个空闲的内存单元都链接到链表中,用于下一次分配。当内存单元被分配时,从链表中删除。 2. 如何分配内存?我们可以实现一个函数来搜索链表并查找足够大的空闲内存块。如果找到,则将其标记为已分配并返回起始地址。如果未找到符合条件的内存块,则返回失败。 3. 如何释放内存?当程序不再需要某个内存块时,可以调用释放函数将其还回链表。释放函数将内存块标记为空闲状态,然后将其插入链表中以便下一次分配使用。 下面是一个简单内存分配与回收的实现: ```C++ struct MemoryBlock { int size; bool is_free; MemoryBlock* next; }; MemoryBlock* first_block; void init_allocator(void* memory_pool, int size) { first_block = (MemoryBlock*)memory_pool; first_block->size = size - sizeof(MemoryBlock); first_block->is_free = true; first_block->next = nullptr; } void* allocate(int size) { MemoryBlock* current_block = first_block; MemoryBlock* previous_block = nullptr; while (current_block) { if (current_block->size >= size && current_block->is_free) { if (current_block->size - size > sizeof(MemoryBlock)) { MemoryBlock* new_block = (MemoryBlock*)((char*)current_block + sizeof(MemoryBlock) + size); new_block->size = current_block->size - size - sizeof(MemoryBlock); new_block->is_free = true; new_block->next = current_block->next; current_block->size = size; current_block->is_free = false; current_block->next = new_block; } else { current_block->is_free = false; } return (char*)current_block + sizeof(MemoryBlock); } previous_block = current_block; current_block = current_block->next; } return nullptr; } void deallocate(void* ptr) { MemoryBlock* current_block = (MemoryBlock*)((char*)ptr - sizeof(MemoryBlock)); current_block->is_free = true; if (current_block->next && current_block->next->is_free) { current_block->size += sizeof(MemoryBlock) + current_block->next->size; current_block->next = current_block->next->next; } if (current_block->is_free && previous_block && previous_block->is_free) { previous_block->size += sizeof(MemoryBlock) + current_block->size; previous_block->next = current_block->next; current_block = previous_block; } } ``` 这个实现使用一个链表来跟踪空闲内存块。当分配内存时,它搜索列表以查找足够大的内存块,并将其分配给程序。当释放内存时,它将内存块标记为空闲状态,并尝试将相邻的空闲块合并为一个更大的块。 总结:内存分配器是一个非常复杂的系统,涉及到很多细节。在实践中,需要对实际应用场景进行深入分析,并针对性地设计内存分配器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值