9

我正在寻找编写一个自我碎片整理内存管理器,其中一个简单的递增堆分配器与一个简单的压缩碎片整理程序结合使用。

粗略的方案是从最低内存地址开始向上分配块,并保持簿记信息从最高内存地址开始向下工作。

内存管理器会传回智能指针——boost 的 intrusive_ptr 对簿记结构来说似乎是最明显的,然后它们本身会指向实际的内存块,从而提供一定程度的间接性,以便可以轻松地移动块。

碎片整理程序将从“生成”书签开始压缩堆以加快进程,并且一次只对固定数量的内存进行碎片整理。指向块本身的原始指针在下一次碎片整理之前一直有效,因此可以自由传递,直到提高性能为止。

对此的具体应用是控制台游戏编程,因此在每一帧的开始或结束时,可以相对安全地完成碎片整理。

所以我的问题是,是否有人将这种分配方案与 STL 结合使用,它是否会像我怀疑的那样完全摧毁 STL。我可以看到 std::list< intrusive_ptr > 在 intrusive_ptr 级别上工作,但是 stl 列表节点本身的分配是否存在以覆盖下一个/上一个指针本身是 intrusive_ptr 或者我只是必须有一个标准堆分配器与这个更动态的分配器并驾齐驱。

4

5 回答 5

6

如果您要在内存中移动对象,那么您不能完全通用地执行此操作。您将只能对知道它们可能被移动的对象执行此操作。您还需要一个锁定机制。在对象上调用函数时,不能移动它。

原因是整个 C++ 模型依赖于位于内存中固定点的对象,因此如果线程正在调用对象上的方法,则该线程暂停并且对象移动,当线程恢复时将发生灾难。

任何持有指向另一个可能被移动的对象(包括其自身的子对象)的原始内存指针的对象都将不起作用。

这样的内存管理方案可能有效,但您必须非常小心。您需要严格执行句柄和句柄->指针锁定语义。

对于 STL 容器,您可以自定义分配器,但它仍然需要返回固定的原始内存指针。您不能返回可能移动的地址。出于这个原因,如果您使用 STL 容器,它们必须是句柄容器,并且节点本身将是普通的动态分配的内存。您可能会发现句柄间接的开销过多,并且在句柄集合的碎片方面仍然存在问题,而不是使用 STL 获得的收益。

使用直接理解您的句柄的容器可能是前进的唯一途径,即便如此,与使用固定在内存中的传统对象的 C++ 应用程序相比,仍然可能存在大量开销。

于 2009-09-20T13:03:36.520 回答
0

如果这是用于控制台游戏编程,那么在运行时禁止无范围的动态内存分配要容易得多。在启动时,但这有点难以实现。

于 2009-09-20T23:46:57.483 回答
0

STL 容器是使用裸指针实现的。

您可以在实例化它们时指定自定义分配器(因此它们使用您的分配器初始化其指针),但是(因为分配的值存储在裸指针中)您不知道这些指针在哪里,因此您不能稍后更改它们。

相反,您可以考虑自己实现 STL 的一个子集:然后可以使用托管指针来实现您的 STL 容器版本。

于 2009-09-20T12:50:27.043 回答
0

另一种众所周知的技术是伙伴系统。你应该看看那个以获得额外的灵感。

于 2009-09-20T16:23:57.407 回答
0

我对此的看法是,如果不得不害怕碎片化,那意味着您正在处理占您内存很大一部分的数据片段,仅凭这一点,您就无法拥有很多数据片段。你已经知道这些会是什么了吗?也许降级并做出更具体的决定会更好,从而减少对其他代码和应用程​​序总体性能的影响?

列表是放入碎片整理内存管理器的一个非常糟糕的例子,因为它是一堆小块,就像大多数其他 STL 数据结构一样。如果这样做,它将产生各种明显的不良影响-包括碎片整理程序的性能下降,间接成本等。IMO唯一有意义的结构是连续的-数组,双端队列,哈希表的主要块,那些东西,而且只有超出一定的大小,并且只有在它们不再被调整大小之后。这类事情再次需要特定的解决方案,而不是通用的解决方案。

评论一下结果如何。

于 2009-11-14T13:30:25.530 回答