8

我的应用程序需要大量内存和大数据结构才能执行其工作。应用程序通常需要超过 1 GB 的内存,在某些情况下,我的客户确实需要使用 64 位版本的应用程序,因为他们有几 GB 的内存。

过去,我可以很容易地向用户解释,如果内存达到 1.6 到 1.7 GB 的内存使用量,那就是“内存不足”或非常接近“内存不足”的情况,他们需要减少内存或移至 64 位版本。

去年我注意到应用程序通常只使用大约 1 GB 的内存就已经耗尽内存。经过一番调查,似乎这个问题的原因是内存碎片。我使用 VMMAP(一个 SysInternals 实用程序)来查看我的应用程序的内存使用情况,并看到如下内容: 地址空间碎片

橙色区域是我的应用程序分配的内存。紫色区域是可执行代码。

正如您在图像的下半部分看到的那样,紫色区域(即 DLL)被加载到许多不同的地址,导致我的内存碎片化。如果我的客户没有大量数据,这不是问题,但如果我的客户有超过 1 GB 的数据集,并且应用程序的一部分需要大块内存(例如 50 MB),它可能会导致内存分配失败,从而导致我的应用程序崩溃。

我的大多数数据结构都是基于 STL 的,并且通常不需要大块的连续内存,但在某些情况下(例如非常大的字符串),确实需要有一个连续的内存块。不幸的是,并不总是可以更改代码以使其不需要如此连续的内存块。

问题是:

  • 如何影响 DLL 在内存中的加载位置,而不在客户计算机上的所有 DLL 上显式使用 REBASE,或者没有显式加载所有 DLL。
  • 有没有办法在您自己的应用程序清单文件中指定 DLL 的加载地址?
  • 或者有没有办法告诉Windows(通过清单文件?)不要分散DLL(我认为这种分散称为ASLR)。

当然,最好的解决方案是我可以从我的应用程序清单文件中影响的解决方案,因为我依赖于 Windows 自动/动态加载 DLL。

我的应用程序是混合模式(托管+非托管)应用程序,尽管应用程序的主要部分是非托管的。

有人建议吗?

4

3 回答 3

5

首先,您的虚拟地址空间碎片不一定会导致内存不足的情况。如果您的应用程序必须分配适当大小的连续内存块,就会出现这种情况。否则碎片的影响应该很小。

你说你的大部分数据是“基于 STL 的”,但是如果你分配一个巨大的std::vector,你将需要一个连续的内存块。

AFAIK 无法在加载时指定 DLL 的首选映射地址。所以只有两个选择:要么变基(DLL 文件),要么自己实现 DLL 加载(这当然不是微不足道的)。

通常您不需要重新设置标准 Windows API DLL 的基础,它们在您的地址空间中非常紧密地加载。碎片可能来自某些第 3 方 DLL(例如 windows 挂钩、防病毒注入等)

于 2011-11-17T16:37:08.743 回答
3

您不能对清单执行此操作,它必须由链接器的 /BASE 选项完成。IDE 中的链接器 + 高级 + 基地址。最灵活的方法是使用 /BASE:@filename,key 语法,以便链接器从文本文件中读取基地址。

填充文本文件的最佳方法是从 Debug + Windows + Modules 窗口。在调试器中加载程序的 Release 版本并加载整个 shebang。Debug + Break All,打开窗口并将其复制粘贴到文本文件中。编辑它以匹配所需的格式,从地址列计算加载地址。在 DLL 之间留出足够的空间,这样您就不必经常调整文本文件。

于 2011-11-17T18:39:44.443 回答
1

如果您能够在加载相关库之前执行一些您自己的代码,那么您可以提前为自己预留一大块地址空间以进行分配。

否则,您需要确定负责的 DLL,以确定加载它们的原因。例如,它们是 .NET、语言的运行时库、您自己的代码还是您正在使用的第三方库的一部分?

对于您自己的代码,最明智的解决方案可能是使用静态链接而不是动态链接。这对于语言运行时也应该是可能的,并且对于第三方库也是可能的。

对于第三方库,您可以从使用隐式加载更改为显式加载,以便加载仅在您保留了地址空间块之后进行。

我不知道您是否可以对 .NET 库做些什么;但是由于您的大部分代码都是非托管的,因此可以消除托管组件以摆脱 .NET。或者,也许您可​​以将 .NET 部分拆分为一个单独的进程。

于 2011-11-18T04:02:32.580 回答