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

没有内部异常。

MemoryFailPoint总是抛出InsufficientMemoryException,即使内存是可用的

您可以依赖此方法正确工作,当您请求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博客文章:内存不足?增加程序可用内存的简单方法