2021-04-01|2021-04-01 内存池的实现方法

这篇文章主要介绍一下内存池的实现方式,这里介绍的是一种比较经典的内存池实现方式,就是链表法实现,具体原理如下:
1,首先内存池无非是提前申请一大块内存片段,之后把这个片段上的指针分配给用户,对于分配来说只要记住已经分配的偏移量即可,每次分配将指针后移申请的内存块长度即可。
2,主要问题在于释放内存,由于不确定用户申请和释放内存的顺序,必须假定申请和释放都在交叠进行,
对于内存池来说,释放的空间必须要可以复用,否则内存池失去了意义;
但是先申请的内存区域可能先释放,这样将出现释放的区域位于已分配区域中间的情形;如何索引这些释放的内存片段是内存池要主要解决的问题;
见下图:

2021-04-01|2021-04-01 内存池的实现方法
文章图片
未命名文件.jpg 图中X区域先申请,当申请了Y区域之后,释放了X区域,此时如和能够复用X区域的内存,而不是只能使用Y区域之后的内存时要解决的问题;
内存池的链表法实现见第二幅图,每个申请的区域前有一个固定长度的头部,其中记录了一些信息,最重要的是两个指针,一个指向后面的内存块头部,另一个指向前面的内存块头部,并且末尾的内存块和首部链接起来,构成一个双向环形链表结构;
同时还有个标记位标志这块内存有没被使用;
当申请内存时,从链表首部开始遍历找到第一个没有使用的内存块,判断其大小是否满足,如不满足,继续搜索;直到满足条件,此时分2种情形:
1,可用内存块大小和要申请的大小一样,则直接标记为已使用,并返回即可
2,可用内存块大于要申请的内存块,则把可用内存区间分裂为2部分,前一部分标记为已使用,后一部分标记为未使用,并继续保持它们的链表结构;
对于释放内存,需要判断是否前面和后面相邻的内存块有未使用,如果有,则需要合并,这一步至关重要,因为如果存在大量相邻的标记未使用内存可能会导致无法分配一块较大区域,
以上操作始终保持链表结构,就可以实现一个基础内存池了;
实际上,这个实现思路来自于John Carmack在Quake3中的实现
但是我们可以稍微做一点优化(不过要牺牲一些额外的存储空间)优化方法是,额外记录两个指针,以环形形式连接所有未使用的内存空间,这样,查找的效率提高了,几乎提升到了常数时间,
同时这个内存池还有一个缺陷就是长度固定,不过顺着这个思路,我们可以在内存不够时,新分配一块大的区域,并且保持原来的链表结构即可,
【2021-04-01|2021-04-01 内存池的实现方法】下面附上代码实现,见
https://github.com/wiltchamberian/MemoryPool
这里实现了两个,第一个是参考Quake3 Carmack的实现,应该无问题;
第二个SuperMemoryPool是改良后的实现,改良版实现的速度要提高了一些,支持动态内存

    推荐阅读