65.9K
CodeProject 正在变化。 阅读更多。
Home

使用自定义类实现的固定内存池分配

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2018年11月30日

CPOL

3分钟阅读

viewsIcon

10408

downloadIcon

191

一个用 C++ 编写的通用固定内存池类

引言

内存池允许以恒定执行时间分配内存。池中数千个对象的内存释放只需一个操作(块),而如果使用 malloc 为每个对象分配内存,则需要逐个释放。

内存池可以组织成分层树结构(如本例所示),这适用于循环和递归等特殊编程结构。

固定大小的块内存池不需要为每个分配存储分配元数据,例如分配块的大小。特别是对于小分配,这可以节省大量空间。

允许在实时系统上实现确定性行为,避免内存不足的错误。

适用场景

假设你有一个预先分配的内存大缓冲区,我们称之为池。
假设你想将东西放入这个池中,但你想控制所有东西的位置,并且你不知道如何做或者没有时间去做。
假设你需要一个东西来告诉你池内每个小缓冲区可以放置的位置,并知道它的大小。

但你不在乎如何以及在哪里,你只需要一个好的东西来告诉你位置。
那么这个类可能适合你的需求。

例如,在 OpenGL 环境中,你可以分配一个足够大的缓冲区并使用此缓冲区大小初始化池(例如,用于 vbo 缓冲区),然后可以使用此类将你的大缓冲区管理成最小的块(这实际上是我当前使用此类的其中一种方式),以管理一个大的预分配缓冲区(池)到最小的内存区域(或窗口),这个类不会更改内存中的任何内容,如果你想移动东西,由你决定。

在这种环境中,你可以访问 GPU 内存,但 GPU 不会跟踪内存区域。
因此,这个类将满足你的需求。

另一种可能的用途是管理一个大型映射内存区域,该区域在进程之间共享或不共享,例如内存映射文件。

Using the Code

使用这个类非常简单。你首先需要初始化池并调用 InitPool 方法。

所以首先,声明一个 pool 对象,例如

MemoryPool m_Pool;

然后,使用所需的参数调用 InitPool 方法

    // Pool initializer, this function must be called after pool object is declared
    // you should pass the size of the pool you want to manage
    // base is the "address" you want to start the pool usually will be the address 
    // of your large buffer if you want to get addresses when invoking Allocate
    // or 0 in which case values returned by Allocate will be equivalent to 
    // relative offsets measured in bytes
    // to the real address of your big buffer

    virtual bool InitPool(uint64_t size, uint64_t base =0, uint64_t minSplitSize=1024);

例如

m_Pool.InitPool(1024*1000,0);

这将创建一个从基地址 0 开始的 1MB 池。然后,要请求池中的空间,例如,你需要 2048 字节

uint64_t offset =m_Pool.Allocate(2048);

然后释放此内存

m_Pool.Free(offset);

就这样了!!

兴趣点

类源代码中包含一个测试套件应用程序,你可以通过它了解其工作原理。向用户呈现池的可视化地图,这是通过一个自定义控件完成的,当然,这对于其他用途来说也是一个不错的选择。

此测试套件报告内存池状态并允许测试类的所有功能。

为了跟踪分配的块,使用了 std::map。池会尝试尽可能重用释放的块,并且如果需要,还会尝试分割它们,这样可以避免内存碎片。

© . All rights reserved.