内存池
2026/8/23大约 2 分钟
内存池
内存池的核心思想:
提前申请一大片内存,再切成许多固定大小的小块,申请时拿一块,释放时还一块。
一、为什么需要内存池?
直接使用:
malloc();
free();可能产生:
内存碎片
分配时间不确定
频繁申请释放带来开销而内存池提前准备好内存:
大块内存
┌────┬────┬────┬────┬────┐
│32B │32B │32B │32B │... │
└────┴────┴────┴────┴────┘申请和释放都直接操作固定大小的 block。
二、最简单的内存池
#define POOL_SIZE 100
#define BLOCK_SIZE 32
static uint8_t pool[POOL_SIZE][BLOCK_SIZE];
static uint8_t used[POOL_SIZE];这里:
100 个 block
每个 block 32 字节
总容量 = 3200 字节申请:
void *pool_alloc(void)
{
for (int i = 0; i < POOL_SIZE; i++)
{
if (!used[i])
{
used[i] = 1;
return pool[i];
}
}
return NULL;
}找到空闲块就返回。
释放:
void pool_free(void *ptr)
{
int idx = ((uint8_t *)ptr -
(uint8_t *)pool) / BLOCK_SIZE;
used[idx] = 0;
}释放后,这个 block 又可以被重新申请。
三、为什么不会产生外部碎片?
因为所有 block 大小固定:
[32][32][32][32][32][32]释放:
[已用][空闲][已用][空闲][已用]新的申请直接拿一个空闲 block。
不会出现:
[10B空闲][20B已用][15B空闲][30B已用]这种大小不一的碎片。
所以:
固定块内存池天然适合管理大量相同大小的对象。
四、为什么适合实时系统?
简单实现中:
for (...)需要遍历查找空闲块,最坏情况是 O(n)。
可以进一步使用空闲链表:
free_list
↓
[block] → [block] → [block] → NULL申请:
取链表头
↓
O(1)释放:
放回链表头
↓
O(1)这样分配和释放时间更加稳定,适合实时系统。
五、内存池和 malloc 的区别
malloc/free
→ 大小灵活
→ 可能产生碎片
→ 执行时间不一定稳定
内存池
→ 固定大小
→ 几乎没有外部碎片
→ 分配/释放时间可预测因此在:
- MCU
- RTOS
- 驱动
- 网络协议栈
中都很常见。
六、核心记忆
内存池
↓
预先准备一大片内存
↓
切成固定大小 block
↓
alloc → 拿一个
free → 还一个内存池的本质,就是用“固定大小的内存块 + 空闲管理机制”,换取更稳定、更可预测的内存分配。

