使用自定义类实现的固定内存池分配
一个用 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
。池会尝试尽可能重用释放的块,并且如果需要,还会尝试分割它们,这样可以避免内存碎片。