内存池是什么?
内存池(Memory Pool)是一种内存管理技术,它预先分配一大块内存,然后将其分成多个固定大小的小块。这些小块被组织起来,用于程序在运行期间频繁进行的内存分配和释放操作。内存池通过创建一个“池子”来管理这些小块,以便在需要时快速分配和回收内存,而不需要每次都调用系统的内存分配函数(如 malloc()
和 free()
)。
内存池的基本概念是分配一次,重复使用。通过预先分配一大块内存,程序可以在需要时从这个预先分配的内存中快速获取内存块,并在不使用时归还给池子,从而减少频繁的分配和释放操作。
使用内存池有什么好处?
-
提高性能:
系统的内存分配函数(如malloc()
和free()
)在操作系统级别会涉及复杂的管理操作,尤其是在高频率分配和释放内存时,开销可能很大。
内存池通过提前分配和复用内存块,减少了频繁的系统调用,从而显著提高性能。特别是在实时系统或需要处理大量小对象的系统中,内存池的性能提升尤为显著。 -
减少内存碎片:
当使用系统的内存分配函数时,如果程序频繁分配和释放不同大小的内存块,可能会导致内存碎片化,即分散的小块可用内存,无法被有效利用。
内存池通常管理的是大小固定的内存块,因此不会产生内存碎片问题。 -
防止内存泄漏:
在某些情况下,内存池可以帮助减少内存泄漏的风险。因为内存池的内存块是固定的,开发者不需要担心忘记释放特定的内存块,只需要在适当的时候释放整个内存池。
在不同的业务场景下内存池通常也都不一样,在初学内存池阶段,我们实现一个固定块的内存池(在内存池里分配大小一样的小块)。下边是实现过程:
如果释放某个内存块以后,我们下一个可以分配的内存块应该指向刚刚释放的那个内存块,问题是怎么找到它呢?可以通过二级指针实现链表,使每个小内存块的前边存放下一个可以分配的内存块的地址。
函数实现:
#include <stdio.h>
#include <stdlib.h>
// 8K的大小
#define MEM_PAGE_SIZE 8192
typedef struct mempool_s {
int block_size;//固定小内存块的大小
int free_count;//内存池中还能分配的内存块的数量 剩余还有多少块
char *free_ptr;//下一个内存块可以分配的地址
char *mem;//指向分配的内存池的起始地址 指向整块内存
} mempool_t;
//内存池初始化
int mp_init(mempool_t *m, int size) {
if (!m) return -1;
if (size < 16) size = 16;
m->block_size = size;
m->mem = (char *)malloc(MEM_PAGE_SIZE);//分配整块内存
if (!m->mem) return -1;
m->free_ptr = m->mem;
m->free_count = MEM_PAGE_SIZE / size; //能够分配固定快的大小
int i = 0;
//二级指针实现单项链表
char *ptr = m->free_ptr;
for (i = 0;i < m->free_count;i ++) {
*(char **)ptr = ptr + size;
ptr += size;
}
*(char **)ptr = NULL;
return 0;
}
//内存池销毁
void mp_dest(mempool_t *m) {
if (!m || !m->mem) return ;
free(m->mem);
}
//分配
void *mp_alloc(mempool_t *m) {
if (!m || m->free_count == 0) return NULL;
void *ptr = m->free_ptr;
m->free_ptr = *(char **)ptr;
m->free_count --;
return ptr;
}
//释放
void mp_free(mempool_t *m, void *ptr) {
*(char **)ptr = m->free_ptr;
m->free_ptr = (char *)ptr;
m->free_count ++;
}
int main() {
mempool_t m;
//分配大小为64字节的固定块
mp_init(&m, 64);
void *p1 = mp_alloc(&m);
printf("1: mp_alloc: %p\n", p1);
void *p2 = mp_alloc(&m);
printf("2: mp_alloc: %p\n", p2);
void *p3 = mp_alloc(&m);
printf("3: mp_alloc: %p\n", p3);
void *p4 = mp_alloc(&m);
printf("4: mp_alloc: %p\n", p4);
mp_free(&m, p2);
void *p5 = mp_alloc(&m);
printf("5: mp_alloc: %p\n", p5);
return 0;
}