c++ - 内存池背后的常见实现细节是什么?

标签 c++ memory-management memory-pool

我试图了解使用内存池进行内存管理,但我 找不到太多关于它的信息,即使它似乎很常见 机制。

我所知道的只是“内存池,也称为固定大小的 block 分配”根据维基百科,我可以使用这些 block 来分配 我的对象的内存。

有没有关于内存池的标准规范?

我想知道这在堆上是如何工作的,它是如何实现的 实现了,应该如何使用?

来自 this ques­tion about C++11 mem­ory pool de­sign pat­terns ,我读过:

In case you haven't al­ready, fa­mil­iar­ize your­self with Boost.Pool. From the Boost doc­u­men­ta­tion:

What is Pool?

Pool al­lo­ca­tion is a mem­ory al­lo­ca­tion scheme that is very fast, but lim­ited in its us­age. For more in­for­ma­tion on pool al­lo­ca­tion (also called sim­ple seg­re­gated stor­age, see con­cepts con­cepts and Sim­ple Se­gre­gated Stor­age.

我能理解他的意思,但这并不能帮助我理解如何 使用它们以及内存池如何帮助我的应用程序,如何实际 充分利用它们。

如果有一个简单的例子来说明如何使用内存池,我们将不胜感激。

最佳答案

任何类型的“池”实际上只是您提前获取/初始化的资源,以便它们已经准备好使用,而不是随每个客户请求动态分配。当客户端完成使用它们时,资源会返回到池中而不是被销毁。

内存池基本上只是您预先分配的内存(通常是大块)。例如,您可能会提前分配 4 KB 的内存。当客户端请求 64 字节的内存时,您只需交给他们一个指向该内存池中未使用空间的指针,以便他们读取和写入他们想要的任何内容。客户端完成后,您可以再次将那部分内存标记为未使用。

作为一个基本示例,它不关心对齐、安全或将未使用(释放)的内存返回到池中:

class MemoryPool
{
public:
    MemoryPool(): ptr(mem) 
    {
    }

    void* allocate(int mem_size)
    {
        assert((ptr + mem_size) <= (mem + sizeof mem) && "Pool exhausted!");
        void* mem = ptr;
        ptr += mem_size;
        return mem;
    }

private:
    MemoryPool(const MemoryPool&);
    MemoryPool& operator=(const MemoryPool&);   
    char mem[4096];
    char* ptr;
};

...
{
    MemoryPool pool;

    // Allocate an instance of `Foo` into a chunk returned by the memory pool.
    Foo* foo = new(pool.allocate(sizeof(Foo))) Foo;
    ...
    // Invoke the dtor manually since we used placement new.
    foo->~Foo();
}

这实际上只是从堆栈中汇集内存。更高级的实现可能会将 block 链接在一起并进行一些分支以查看 block 是否已满以避免内存不足,处理作为 union 的固定大小的 block (空闲时列出节点,使用时为客户端提供内存),以及它肯定需要处理对齐(最简单的方法就是最大对齐内存块并为每个 block 添加填充以对齐后续 block )。

更花哨的是伙伴分配器、slab、应用拟合算法的分配器等。实现分配器与数据结构没有太大区别,但你会深入原始位和字节,必须考虑对齐等问题,并且不能随机播放内容(不能使指向正在使用的内存的现有指针无效)。像数据结构一样,没有真正的黄金标准说“你应该这样做”。它们种类繁多,各有优缺点,但有一些特别流行的内存分配算法。

实际上我会向许多 C 和 C++ 开发人员推荐实现分配器,只是为了更好地适应内存管理的工作方式。它可以让您更加了解所请求的内存如何连接到使用它们的数据结构,并且还可以在不使用任何新数据结构的情况下打开一扇全新的优化机会之门。它还可以使通常效率不高的链表等数据结构更加有用,并减少使不透明/抽象类型不那么不透明以避免堆开销的诱惑。然而,最初的兴奋可能会让你对所有东西都使用自定义分配器,但后来才后悔额外的负担(特别是如果你兴奋地忘记了线程安全和对齐等问题)。值得在那里放松一下。与任何微优化一样,它通常最好是离散地应用,事后看来,并且手头有分析器。

关于c++ - 内存池背后的常见实现细节是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30508183/

相关文章:

java - 在java环境下解析C++源码

c++ - const 之前还是 const 之后?

c++ - 我如何将 boost::spirit X3 与 QString 结合使用?

c++ - 如果内存池比malloc快,为什么malloc不能在幕后使用它们?

c++ - 您如何声明和使用重载的池运算符删除?

c++ - MongoDB 示例无法使用不同的标志进行编译

memory-management - 当进程访问刚用 brk/sbrk 分配的地址时,内核会发生什么?

c - 如何索引障碍分配

performance - 有关GC的任何硬数据与显式内存管理性能之间的关系?

c - posix_memalign/memalign 做什么