文章

内存池

内存池

内存池

内存池(Memory Pool)*是一种*提前分配一大块内存、并在之后反复复用这块内存来分配/释放对象的技术,目的是为了提高性能、降低内存碎片、避免频繁调用 malloc/new 等昂贵操作。

一、为什么要用内存池

标准的 new / malloc 调用:

  • 都涉及系统调用(如 sbrk, mmap),慢;
  • 易产生碎片;
  • 无法保证内存地址的连续性;
  • 每次创建/销毁对象都有构造/析构与堆管理开销。

内存池的优势:

优点描述
快速分配内部通过指针或索引跳转,分配速度远快于 malloc/new
控制碎片一次性分配大内存块,减少碎片
可重用对象销毁后可复用内存,无需反复系统分配
可预测性高特别适合实时系统、游戏、数据库、嵌入式等对性能要求极高的场景

二、工作原理概览

  1. 预分配一块大内存(如 char pool[1024 * 1024]

  2. 把这块内存分成若干个固定大小的块(或管理可变大小)

  3. 维护一个空闲链表/栈来跟踪哪些块可用

  4. alloc():从空闲列表中拿出一块,构造对象(用 placement new

  5. free():调用析构函数,把内存块归还空闲列表

三、简单示例:固定大小内存池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 内存池模板类:管理固定数量(N)对象(类型为 T)的内存复用
template <typename T, size_t N>
class MemoryPool {
private:
    // 原始内存块,用于存储 N 个对象的原始字节
    // alignas(T) 确保 buffer 对齐到类型 T 的对齐要求,避免未定义行为
    alignas(T) char buffer[N * sizeof(T)];

    // 空闲列表,保存当前可用的对象地址
    std::vector<T*> free_list;

public:
    // 构造函数:初始化空闲列表,将 buffer 切分成 N 个对象大小的块
    MemoryPool() {
        for (size_t i = 0; i < N; ++i) {
            // 将 buffer 中每个对象位置转换为 T* 并加入空闲列表
            free_list.push_back(reinterpret_cast<T*>(&buffer[i * sizeof(T)]));
        }
    }

    // 分配一个对象
    T* allocate() {
        // 如果没有可用内存,抛出异常
        if (free_list.empty()) throw std::bad_alloc();

        // 从空闲列表中取出一个空闲块
        T* ptr = free_list.back();
        free_list.pop_back();

        // 在该内存位置上使用 placement new 构造对象
        return new (ptr) T();
    }

    // 回收一个对象
    void deallocate(T* ptr) {
        // 显式调用析构函数,释放对象资源
        ptr->~T();

        // 将内存块重新加入空闲列表,以便后续复用
        free_list.push_back(ptr);
    }
};

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct MyObject {
    int x;
    MyObject() { std::cout << "Constructor\n"; }
    ~MyObject() { std::cout << "Destructor\n"; }
};

int main() {
    MemoryPool<MyObject, 10> pool;

    MyObject* obj1 = pool.allocate(); // 从池中分配一个对象
    pool.deallocate(obj1);            // 回收对象

    return 0;
}

四、STL 容器也支持内存池

STL 的 allocator 机制允许我们自定义内存分配器:

1
std::vector<int, MyMemoryPoolAllocator<int>> vec;

这让 vector/map/set 等容器内部元素也能通过内存池分配,提高性能。

五、注意事项

项目说明
析构函数需手动调用内存池只分配空间,不会自动析构对象
不支持动态大小对象固定块大小的内存池不适合 std::stringvector 等变长对象
线程安全性多线程环境需加锁或使用线程局部内存池
内存泄漏风险使用错误容易忘记回收或析构对象
本文由作者按照 CC BY 4.0 进行授权