【发布时间】:2014-05-06 17:30:18
【问题描述】:
我们在使用 HeapCreate()/HeapAlloc() 进行大分配时遇到问题 (> 512K)
我们正在开发一个 C++ 服务器应用程序,同时对一些图像执行一些“图像处理”操作。它应该可以工作很长时间而无需重新启动。
我们的处理模型非常具体。 服务器启动,执行一些必要的分析以检测最大值。给定硬件配置的并发图像数量,这意味着以最佳性能稳定工作,快速达到最大负载,然后在大多数时间或多或少地以相同的高负载工作,具体取决于输入队列。
这意味着我们在开始时使用了所有需要的内存,并且内存总量不应该增加(如果一切正常的话)。 我们的痛苦是分裂。传入图像的大小可能从 400K 到可能的 50M 不等,并且每个图像的处理导致相应的(与图像大小成比例)相对较大的 OPENCV 分配。处理场景(和相关分配)会有所不同,取决于图像细节,分配/释放操作非常密集,然后在一段时间后我们会得到碎片。鉴于微不足道的改进,开发了一些局部优化。 实际上,大约在大约之后,我们会产生内存不足/碎片相关的影响。 50000-70000 张图片,这是不可接受的。当前的解决方案是重新启动服务器,这远非理想。
解决问题的最初天真的建议是:
- 我们有自己的自定义堆,最初提交整个所需的内存。
- 所有必需的“大”OPENCV 分配(并且只有那些)重定向到此堆
- 此时,碎片到达,我们停止新输入并完成所有正在运行的作业。
- 这意味着所有与图像相关的分配都已释放。
- 检查堆并在需要时清理它(例如由于内存泄漏)
- 现在,我们的堆完全是空的,可以从头开始。再次打开输入。
简单的概念验证项目很快就发现了以下内容:
- HeapCreate(),最初提交 250M,每次我从中调用 HeapAlloc() 时增长 10M!很奇怪,不是吗?
- 正如使用 HeapWalk() 所识别的那样,提交的内存不是保留在一个连续的块中,而是保留为超过 500 个块的列表,每个块 512K。所以它们都不适合我的 10M 请求和调用堆来处理未提交的内存
似乎 Win32 自定义堆仅针对小型分配进行了优化,我无法找到一种方法来满足我的需要 :( VirtualAlloc() 似乎是一个解决方案,但它是非常底层的 API,使用它意味着开发我自己的内存管理系统,似乎是某种轮子改造。
我想相信存在一些标准方法,但我找不到它。 任何帮助或相关资源阅读将不胜感激
【问题讨论】:
-
抱歉初始格式。这是一个技术问题。
-
这个问题在今天根本无法解决。重新构建程序以针对 x64。
-
@HansPassant:重新定位到 64 位模型可能只是推迟问题而不是解决问题。如果一个程序有一个有害的碎片问题并且它必须无限期地运行,它最终会耗尽虚拟地址空间并且它可能会耗尽可用的存储空间(如果碎片严重到无法释放页面)。即使延期很长时间,业绩也可能会出现流失。
-
不,分割一个 256 TB 的地址空间,却找不到一个 50 MB 的漏洞,代码在 2 GB 的空间中工作是完全不可能的。
-
@Hans Passant:Windows x64 的用户模式虚拟地址空间为 8 TB,而不是 256。由于碎片问题,该应用程序无法在 2 GB 空间中无限期地运行;因此问题。如果碎片严重到足以防止内存未提交,您最终将达到提交限制(通常约为 1 TB)。
标签: c++ windows memory-management heap-memory fragmentation