初识内存池

  在程序开发过程中,我们总会涉及到一个概念,那就是内存管理(一般值堆内存)。一旦由内存使用和管理不当导致程序运行宕机,会发生无法预测的灾难。内存问题分析比较困难,因为大多数时候内存操作对于我们是透明的,一般只有malloc/calloc和free接口供我们使用。

  为了辅助管理堆内存,内存池的概念被提出。内存池的出现是针对内存频繁分配和缩放导致的内存碎片化问题。

  在服务器上,我们可以知道的内存池使用场景可以有:

    • 全局内存池(必须做小块内存回收)
    • 对每个网络连接申请一个内存池(可以不做小块内存回收)
下面,针对每个网络连接申请一个内存池的应用场景,我们来聊聊内存池。

一、第一个版本的内存池

struct memNode{
   void* addr;    //指向内存地址
   int size;       //内存大小
   int flag;      //内存空闲标志,0为空闲
   struct memNode* next;   //指向下一块内存管理节点
};

  此版本中,使用链表节点串联内存管理节点,当free的时候,将flag标志置0。当malloc时,在链表中查找flag == 0 且大小合适的节点,如果不存在才再次向系统申请内存。

  但是,此版本的内存池,每次申请的内存大小不可控,不便于管理。

二、第二个版本的内存池

  在版本二中,我们规定一些固定的大小,比如

16    byte
32    byte
64    byte
128   byte
256   byte
512   byte
1024  byte

  每次向系统申请固定大小的内存。比如,当用户申请内存为50 byte时,内存池向系统申请64byte的内存空间。

  那么,当用户申请的内存大小size超过我们所定义的最大值呢?此时,我们直接向系统申请size大小的内存,这块内存我们称为大块内存。前面定义的固定大小的内存块称为小块内存。

  此时,内存池的存储结构可以使用哈希+链表。

 

  对于大块内存,可以不放到上述的存储结构,单独保存;也可以在最后增加一个索引指向大块内存链表。

  小块内存重复使用,大块内存会及时释放。

   但是,此版本的内存池还是存在明显的问题。

  1. 查找速度慢
    • 申请内存时查找空闲节点(此问题可以很方便的解决:将节点分为非空闲节点和空闲节点分别用两条链表保存)
    • 释放内存时查找指定节点(此问题可以通过选择合适的存储结构存储管理节点,比如红黑树或哈希)  
  2. 不利于内存回收
    • 此处的内存回收指对于零散的小块内存空间无法回收合并成大块内存空间使用

三、第三个版本的内存池

  此版本优化有如下几点。

    • 使用一块固定的大内存做小块内存申请,然后当该空间的所有内存都是空闲的时候,再释放整块大内存
    • 内存管理节点本身也是一个小内存,所以将内存管理节点也放到申请的小内存聚合块中
    • 大块内存的管理节点,作为一个小块内存分配在小内存聚合块中

  在实现上,我们选择在每一个小内存聚合块中创建一个内存管理节点,管理当前块的小内存分配。对于小内存的释放不做操作,其小内存生命周期与内存池一致。

 

   (注:mp_large_s管理节点会放在小内存聚合块中)

  各结构体定义如下。

struct mp_large_s{  //大块内存管理节点
    struct mp_large_s    *next;
    void *alloc;        //指向内存
};

struct mp_node_s{  //小块内存管理节点(此结构体会放在小块内存聚合块的前面)
    unsigned char *last;    //聚合块可用内存的头指针
    unsigned char *end;        //聚合块可用内存的尾指针

    struct mp_node_s *next;
    size_t failed;    //用于判断该聚合块是否还可以给小块内存分配使用
};

struct mp_pool_s{
    size_t max;            //当申请的内存小于max时申请小块内存

    struct mp_node_s *current;    //指向小块内存管理节点
    struct mp_large_s *large;    //指向大块内存管理节点

    struct mp_node_s head[0];    //柔性数组
};

  内存池对外提供如下接口。

struct mp_pool_s *mp_create_pool(size_t size);
void mp_destory_pool(struct mp_pool_s *pool);
void *mp_alloc(struct mp_pool_s *pool, size_t size);
void *mp_nalloc(struct mp_pool_s *pool, size_t size);
void *mp_calloc(struct mp_pool_s *pool, size_t size);
void mp_free(struct mp_pool_s *pool, void *p);   //内部实现只释放大块内存

==================================================

版本三代码:https://gitee.com/mad-cat/MemoryPool.git

posted @ 2022-10-18 23:24  幻cat  阅读(73)  评论(0编辑  收藏  举报