保留内存是否会导致内存不足异常?

10
我们有一个32位的Windows服务存在内存泄漏问题,会抛出OutOfMemory异常。它是在Windows Server 2003上运行的.NET 4.0可执行文件。在使用WinDbg调试崩溃转储文件时,我发现大部分内存实际上是被保留而不是提交的。
如您从WinDbg截图中所见,有2.5GB未分类的内存使用情况,其中2.1GB实际上是保留内存(MEM_RESERVE)。我有调试转储文件的经验,但这种情况对我来说是新的。MEM_COMMIT很好-564.270MB,托管堆的大小约为82MB。
我还检查了本地堆,看是否有大块数据被保留,但也没有发现任何可疑的东西。
所以我的问题是 - MEM_RESERVED是否可能导致OOM异常?如果是这样,我该如何调试它,看看为什么/如何保留了大量内存?您还会在哪里寻找可能的问题?
如果需要其他信息,请告知我,我将更新我的帖子。
2个回答

7

是的,保留内存可能会触发OutOfMemoryException。尝试分配一些非常大的字节数组,这些数组的内存不会被提交,直到您写入数组的内容。但是,您只需分配这些数组就可以轻松触发OOM。

我不知道实现细节,但由于如果不能满足保留请求,则VirtualAlloc将失败,因此我认为CLR会将其转换为异常。我看不到它如何将失败的保留请求转换为任何有用的内容,因此异常是一个明智的选择。


谢谢您的回复,不知道您是否知道如何在WinDbg中查看占用这个巨大内存的是什么。 - Michael
假设内存被托管对象使用,您可以使用“!dumpheap”和“!do”来检查这些对象。然而,鉴于“!eeheap”的输出显示托管堆很小,我认为其他东西保留了这个内存空间。 - Brian Rasmussen
请问还有什么可以保留内存的吗?我感到困惑,因为我期望“某些东西”要么在托管堆上,要么在本机堆上的某些本机资源 - 但它们都没有指向任何可疑的东西。还有什么可能会保留内存?我很乐意立即进行检查。 - Michael
我所说的“其他东西”指的是“本地”。正如我所说,如果保留的内存支持托管对象,那么您将能够在托管堆上找到这些对象。由于这似乎不是这种情况,因此本地库或运行时本身是候选对象,但我不能确定。您可以通过查看!eeheap的输出来获取有关CLR分配的一些其他信息。 - Brian Rasmussen

4

OutOfMemoryException是指系统无法为您的应用程序分配更多虚拟内存时发生的异常。保留内存是虚拟内存,因此也占用该限制。

在C++中,您可以最简单地尝试:

while(::VirtualAlloc(NULL, 65536, MEM_RESERVE, PAGE_READWRITE) != NULL );
std::cout << "All memory reserved. Now check with tools." << std::endl;

如果VirtualAlloc()返回NULL,则无法分配更多的内存。在WinDbg中,这将显示为:
MEM_RESERVE   32307  7f6f2000 (1.991 Gb)  99.59%   99.56%

然而,VirtualAlloc()不会在堆上分配内存,因此!heap在这种情况下是无用的,并且仅显示默认进程堆:
0:000> !heap
Index   Address  Name      Debugging options enabled
  1:   00440000 

反过来说:堆管理器使用VirtualAlloc()获取内存。另外请注意.NET不使用堆管理器,它也直接使用VirtualAlloc()分配内存并自行管理。因此,由于您可以在!heap的输出中看到它,这不是.NET问题,而是本机内存问题。
在我天真的理解中,gflags设置启用DLL堆标记应该有助于确定堆分配的来源。然而,我的期望是!heap -t命令将简单地显示分配内存的DLL名称,但这种情况并没有实现。

谢谢您的输入。我尝试了!heap -t命令,但它没有给出任何有用的信息。我正在尝试挖掘本地内存,看看能否在那里找到一些线索。欢迎提供任何其他检查建议。 - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接