背景与介绍
优势
方案
先提前申请特定大小的内存,当需要使用时再进行分配,不再使用时先不释放而是统一释放。
设计与实现
组成
- 小块内存block
- 大块内存large
-
API
创建内存池mp_init
- 销毁内存池mp_destory
- 重置内存池mp_reset
-
实现
小块内存
mp_block_ttypedef struct mp_block_s {struct mp_block_s *next;unsigned char *last; // 指向未分配内存起始地址unsigned char *end; // 指向最大分配地址size_t failed;}mp_block_t;
使用链表连接
该小块可分配内存大小:
block->end - block->last大块内存
mp_large_ttypedef struct mp_large_s {struct mp_large_s *next;void *alloc;} mp_large_t;
注意:
链表连接,即所有大块内存使用链表遍历
- 在小块内存中存储大块内存指针
- 仅在内存池首个节点块关联大块内存链表
-
内存池
mp_pool_ttypedef struct mp_pool_s {mp_block_t *block;mp_large_t *large;size_t max; // 小块内存的最大分配大小// 其他,如日志等,可对比Nginx内存池实现mp_block_t head[0]; // 柔性数组}mp_pool_t;
head[],柔性数组,来连接第一个小块内存
-
创建内存池
mmpool_initmp_pool_t* mmpool_init(size_t size) {mp_pool_t *p;if (posix_memalign(&p, MP_ALIGNMENT, size + sizeof(mp_pool_t) + sizeof(mp_block_t)) != 0) {return NULL;}p->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL;p->block = p->head; // 指向第一个小块内存,等价p->block = p->head->last = (unsigned char *)p + sizeof(mp_pool_t) + sizeof(mp_block_t)p->large = NULL;p->head->last = (unsigned char *)p + sizeof(mp_pool_t) + sizeof(mp_block_t);p->head->end = p->head->last + size;p->head->failed = 0;return p;}
不要设计成
int mmpool_init(mp_pool_t *p, size_t size),需要在函数内部申请内存后再返回指针(堆空间)- 使用
posix_memalign而不是malloc:
流程描述:
- 申请内存,大小为池结构+小块内存结构+实际分配:
size + sizeof(mp_pool_t) + sizeof(mp_block_t) - 判断size是否超出一页4k,两中取小
- 对所有结构进行初始化
疑问
- MP_MAX_ALLOC_FROM_POOL = 4k - 1,为什么要减1?
参数中的size推荐为整个池的大小还是仅为首块内存池的大小更好?
销毁内存池
mmpool_destory// 销毁内存池void mmpool_destory(mp_pool_t *pool) {mp_block_s *head, *block;mp_large_t *large;// 1. 释放大块内存for (large = pool->large; large; large = large->next) {if (large->alloc) {free(large->alloc);}}// 2. 释放小块内存, 从第二块开始(第一个与pool连接在一起)head = pool->head->next;while (head) {block = head->next;free(head);head = block;}// 3. 释放池结构(含首个小块内存、小块内存结构)free(pool);}
流程:
释放大块内存
- 释放小块内存, 从第二块开始(第一个与pool连接在一起)
-
重置内存池
void mmpool_reset(mp_pool_t *pool) {mp_block_s *block;mp_large_s *large;// 1. 释放所有大块内存for (large = pool->large; large; large = large->next) {if (large->alloc) {free(large->alloc);}}pool->large = NULL;// 2. 收回所有小块内存空间但不释放(即已申请的内存空间均可重新分配)for (block = pool->head; block; block = block->next) {block->last = (unsigned char *)block + sizeof(mp_block_t);}}
流程:
释放所有大块内存
收回所有小块内存空间但不释放(即已申请的内存空间均可重新分配)
分配内存
流程
比较申请大小与内存池最大来决定在小块还是大块上申请内存
- size <= pool->max时,在小块内存节点上申请
mp_alloc_block():- 遍历小块内存节点链表,是否可在已有小块节点上分配内存
size < block->end - block->last,可以则分配后直接返回 - 不可以则需要在内存池上分配新的小块内存节点,并进行分配
- 遍历小块内存节点链表,是否可在已有小块节点上分配内存
- size > pool->max时,在大块内存节点上申请
mp_alloc_large():
