MemoryFailPoint总是抛出InsufficientMemoryException,即使内存是可用的
本文关键字:内存 InsufficientMemoryException MemoryFailPoint | 更新日期: 2023-09-27 18:17:57
我已经编写了以下代码来检查是否有足够的内存,
while (true)
{
try
{
// Check for available memory.
memFailPoint = new MemoryFailPoint(250);
break;
}
catch (InsufficientMemoryException ex)
{
if (memFailPoint != null)
{
memFailPoint.Dispose();
}
Thread.Sleep(waitSecond * 1000);
}
}
我在Windows 7 64位机器上的控制台应用程序中运行上述程序。
这个方法每10秒调用4次。
最初它工作良好,但2-3小时后,总是有一个InsufficientMemoryException
抛出。我检查了可用内存,显示超过1gb。
我试了很多,但我不能找到为什么会发生这种情况。
下面是堆栈跟踪:
at System.Runtime.MemoryFailPoint..ctor(Int32 sizeInMegabytes)
at SocketListner.AcceptConnection(IAsyncResult res) in H:'Projects'SocketListner.cs:line 308
没有内部异常。
您可以依赖此方法正确工作,当您请求250兆字节时,此异常非常很可能在32位进程中出错。当程序运行了一段时间后,这就很难得到了。
程序绝不会使用OOM崩溃,因为您已经消耗了所有可用的虚拟内存地址空间。它崩溃是因为地址空间中没有足够大的空间来容纳分配。您的代码需要一个足够大的洞来一次性分配250兆字节。当你没有得到异常时,你可以确定这个分配不会失败。
但是250兆字节是相当大的,这是一个非常大的数组。并且很可能由于一个叫做"地址空间碎片"的问题而失败。换句话说,一个程序通常从几个非常大的漏洞开始,最大的大约600兆字节。分配用于存储. net运行时和非托管Windows dll使用的代码和数据之间的可用漏洞。当程序分配更多内存时,这些漏洞就会变小。它可能会释放一些内存,但不会产生一个大洞。你通常会得到两个洞,大约是原来的一半大小,中间的某个地方会把原来的大洞切成两半。
这被称为碎片,一个32位进程分配和释放大量内存,最终将虚拟内存地址空间碎片化,所以一段时间后仍然可用的最大洞变小了,大约90兆字节是相当典型的。要求250mb几乎肯定会失败。你需要把目标放低一点。
您无疑希望它以不同的方式工作,确保分配的和加起来等于250兆字节保证工作。然而,这不是MemoryFailPoint的工作方式,它只检查最大的可能的分配。也许不用说,这让它变得不那么有用了。除此之外,我很同情。net框架的程序员,让它按照我们希望的方式工作既昂贵又不能实际提供保证,因为分配的大小最重要。
虚拟内存是一种非常便宜的丰富资源。但是要想把它全部消耗掉是非常麻烦的。一旦你消耗了1gb的数据,那么随机的OOM攻击就开始变得可能了。不要忘记解决这个问题的简单方法,因为您运行的是64位操作系统。因此,只需将EXE平台目标更改为AnyCPU就可以获得大量的虚拟地址空间。取决于操作系统版本,但tb是可能的。它仍然是碎片,但你只是不再关心,洞是巨大的。
最后但并非最不重要的是,在评论中可以看到,这个问题与无关 RAM。虚拟内存与您有多少RAM无关。将虚拟内存地址映射到RAM中的物理地址是操作系统的工作,它是动态地完成的。访问内存位置可能会触发页面错误,操作系统将为该页分配RAM。相反的情况发生了,当某个页面在其他地方需要时,操作系统会为它解除对RAM的映射。你永远不会耗尽内存,在这之前机器会慢下来。SysInternals的VMMap实用程序可以很好地查看程序的虚拟地址空间,尽管对于大型进程,您可能会淹没在信息中。
MemoryFailPoint检查连续可用内存,如下所示:http://msdn.microsoft.com/fr-fr/library/system.runtime.memoryfailpoint.aspx
您可能消耗很少的内存,但是已经对其进行了大量的碎片化,然后现在无法分配所需大小的连续内存块。几个小时后出现这种问题是很典型的。为了避免这种情况,请使用一个对象池来存放您不断实例化的对象,这将使所使用的内存空间更加刚性。
考虑使用GC.GetTotalMemory
方法来确定调用之前和之后的可用内存量:
memFailPoint = new MemoryFailPoint(250);
InsufficientMemoryException
在开始操作之前由MemoryFailPoint
构造函数抛出,当您指定的预测内存分配大于当前可用内存的数量时。就像user7116评论的那样,这就是为什么你应该先检查。
这个链接中的例子应该给你一个解决方案:MemoryFailPoint Class
您也可以查看msdn博客文章:内存不足?增加程序可用内存的简单方法