Win32 HeapCreate()initialSize不支持大分配

时间:2014-03-27 15:51:27

标签: c++ windows memory-management heap-memory fragmentation

我们在使用HeapCreate()/ HeapAlloc()进行大量分配时遇到问题(> 512K)

我们正在开发一个C ++服务器应用程序,执行一些图像处理'同时对几张图像进行操作。它应该可以长时间工作而无需重新启动。

我们的处理模型非常具体。 服务器启动,执行一些必要的分析以检测最大值。给定硬件配置的并发映像数,意味着稳定工作,具有最佳性能,快速达到最大负载,然后在大多数情况下以相同的高负载运行或多或少,具体取决于输入队列。

这意味着我们在开始时利用所有必需的内存,并且内存总量不应增长(如果一切正常)。 我们的痛苦是碎片化。输入图像的大小可以从400K变化到可能50M,并且每个的处理导致相应的(与图像大小成比例)相对大的OPENCV分配。处理方案(和相关的分配)各不相同,取决​​于图像细节,分配/免费操作是非常密集的,然后一段时间后我们得到碎片。由于改进可以忽略不计,因此开发了一些局部优化。 实际上我们在大约之后会出现内存/碎片相关的影响。 50000-70000图像是不可接受的。目前的解决方案是重新启动服务器,这远非理想。

解决问题的初步天真提议是:

  • 我们有自己的自定义堆最初提交所需的整个内存。
  • 所有必需的'大' OPENCV分配(仅限那些)重定向到此堆
  • 目前,碎片到来,我们停止新的输入并完成所有正在运行的工作。
  • 这意味着所有与图像相关的分配都会被释放。
  • 检查堆并根据需要清理它(例如,由于内存泄漏)
  • 现在,我们有绝对空的堆,可以从头开始。再次打开输入。

简单的概念验证项目很快就找到了以下内容:

  • HeapCreate(),最初提交250M,每次从它调用HeapAlloc()时增长10M!奇怪,不是吗?
  • 正如使用HeapWalk()所识别的那样,已提交的内存不是保留在一个连续的块中,而是作为500个块的列表,每块512K。所以它们都不适合我的10M请求和被调用的堆来处理未提交的内存

似乎Win32自定义堆仅针对小分配进行了优化,我无法找到一种方法来满足我的需求:( VirtualAlloc()似乎是一个解决方案,但它是非常低级的API,使用它意味着开发我自己的内存管理系统,似乎是某种轮子改造。

我想相信存在一些标准方式,我找不到它。 任何帮助或相关资源将非常感谢

1 个答案:

答案 0 :(得分:0)

一些想法:

  1. 堆通常从较大的内存块管理小的子分配。如果您需要大量分配,则堆可能不是解决方案。您可能需要自己动手并直接处理虚拟内存。

  2. 目前尚不清楚大量分配的HeapAlloc是否实际是从堆的保留内存中分配的。 MSDN有点模糊,偶尔也会自相矛盾,但low-fragmentation heap(LFH)上的页面表示大于16 KB的分配不使用LFH。这可能意味着堆会为您跟踪它,但确实满足VirtualAlloc调用的大量分配,而不是来自保留的内存。如果是这样的话,使用堆可能只会让事情变得更糟。 (无论如何,使用和不启用LFH可能值得尝试。)

  3. 如果您的问题往往是碎片而不是实际的内存耗尽,那么您可能最好浪费一些内存以消除碎片。如果您的最大分配需要50 MB,那么您可能会考虑将所有分配大50 MB,即使图像要小得多。平均而言,您将分配较少的块(因此您无法同时处理多个图像),但如果分配的大小始终相同,您将永远不会得到碎片。这是否可接受的权衡取决于您的具体情况。你可以妥协并拥有一堆X大小的块来处理较小的块,如果它们更常见,只有一些大小为Y来处理尽可能大的块。

  4. 另一种方法是平铺,但这会极大地影响应用程序的架构。这个想法是使用固定大小的瓷砖而不是可变尺寸的图像。根据图块大小,根据需要将图像切割成多个图块。瓷砖是独立处理的,输出图像是从瓷砖中重新组装的。由于所有瓷砖尺寸相同,因此可以避免碎片。有些图像处理非常适合这种情况,但其他类型则不然。