首页 > 系统相关 >初识内存池

初识内存池

时间:2022-10-18 23:48:03浏览次数:64  
标签:struct 初识 内存 size 节点 pool mp

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

标签:struct,初识,内存,size,节点,pool,mp
From: https://www.cnblogs.com/unrealCat/p/16804619.html

相关文章

  • js栈内存和堆内存
    js栈内存和堆内存的区别栈(stack)会自动分配内存空间,栈内存变量基本上用完就会自动释放。堆(heap)动态分配的内存,大小不定也不会自动释放,只有当所有调用的变量全部销毁之......
  • 模块的初识
    今日内容概要索引取值与迭代取值的差异模块简介导入模块的两种句式导入模块的句式补充循环导入问题及解决策略判断文件类型模块的查找顺序模块的绝对导入与相对导......
  • 【备忘】Eclipse内存泄漏分析工具MAT - Memory Analyzer Tools
    下载https://www.eclipse.org/mat/downloads.phpLinux上生成HeapDump使用jdk的命令/path/to/jdk/bin/jmap-dump:format=b,file=<文件名XX.hprof><pid>为MAT配置JD......
  • 0 基础晋级 Serverless 高手课 — 初识 Serverless(上)
            应用-无服务器  2017-2006函数即服务类似云计算(Serverless)  faas函数服务+后端数据库账号服务   弹性,按量       服务器,客户端的终......
  • 【C语言知识碎片】动态内存分配函数的使用
    1.为什么需要动态内存分配我们需要存储一些数据时可以创建一个变量或者数组来进行存储。intval=10;intarr[10]={0};数组在开辟好之后大小是不能变的,但是这种静态的内存在......
  • C#操作CPU内存时 winIO32位,64位的使用(运行时要用管理员身份)注意事项
    一、WinIo说明WinIO程序库允许在32位的Windows应用程序中直接对I/O端口和物理内存进行存取操作。通过使用一种内核模式的设备驱动器和其它几种底层编程技巧,它绕过了Wi......
  • String内存泄漏
    内存泄漏:指为一个对象分配好内存之后,在对象已经不再使用时未及时的释放,倒是一直占据内存单元,使实际可用内存减少,就好像内存泄漏了一样。内存溢出:内存不够用了,比如在一个无......
  • 初识反射
    反射:Reflection对象------>类信息Class类是一切反射的根源。Class类表示什么?很多的人---可以定义一个Person类(有年龄、性别、姓名等)很多的车---可以定义......
  • "0x00a1bdb3" 指令引用的 "0x00000001" 内存。该内存不能为 "read"。
    笔记本换成XP系统后,单击我的电脑或者别的时候,有时会提示,下面的错误提示:---------------------------IExplore.exe-应用程序错误---------------------------"0x00a1bdb3"......
  • Java内存模型(JMM)详解
    目录什么是JMM?Java运行时内存区域与硬件内存的关系Java内存区域和Java内存模型有何区别?Java线程与主内存的关系什么是主内存?什么是本地内存?线程间通信重温Java并发三......