【问题标题】:Difference between actual OOM and 2GB object OOM?实际OOM和2GB对象OOM的区别?
【发布时间】:2021-03-05 13:33:04
【问题描述】:

我想知道实际OOM(内存耗尽)引发的OOM异常与2GB object limit is hit时引发的异常之间是否存在差异?

我有以下导致 OOM 的代码(没有 app.config 更改,gcAllowVeryLargeObjects 默认设置为 false):

struct Data
{
    double a;
    double b;
}

// Causes OOM due to the 2GB object limit.
List<Data> a = new List<Data>(134217725);

// Causes OOM due to actual OOM.
List<Data[]> b = new List<Data[]>();
for (int i = 0; i < 13421772; i++)
{
    b.Add(new Data[134217724]);
}

现在,我已从 Visual Studio 执行代码,但出现以下异常:

  • 2GB 对象限制
System.OutOfMemoryException
  HResult=0x8007000E
  Message=Exception of type 'System.OutOfMemoryException' was thrown.
  Source=mscorlib
  StackTrace:
   at System.Collections.Generic.List`1..ctor(Int32 capacity) 
   at ConsoleApp1.Program.Main(String[] args)
  • 实际OOM
System.OutOfMemoryException
  HResult=0x8007000E
  Message=Exception of type 'System.OutOfMemoryException' was thrown.
  Source=ConsoleApp1
  StackTrace:
   at ConsoleApp1.Program.Main(String[] args)

从这里看来,这两个异常(堆栈跟踪/源除外)之间似乎没有显着差异。

另一方面,我从 LINQPad 执行了完全相同的操作并得到以下结果:

  • 2GB 限制

  • 实际OOM

从两个地方执行RuntimeInformation.FrameworkDescription 会导致.NET Framework 4.8.4341.0

我的问题是关于检测/区分这两种情况,尽管我也很好奇为什么 LINQPad 和 VS 执行之间的错误消息不同。

【问题讨论】:

  • 除了学术好奇心之外,您对这个问题有什么目的吗?您使用 VisualStudio 和 linqPad 的目标是什么版本的 .net?
  • 发生在herehere。元素的数量可能会溢出并变为负数。嗯。
  • @JonasH .net 版本都是.NET Framework 4.8.4341.0。我想知道是否可以以某种方式检测到不同的情况,因为它们代表不同的状态:一个无法帮助(实际 OOM)并提出应用程序设计问题,而另一个(2GB 大小限制)可以通过不同的方式解决处理单个数据结构(如果可以检测到异常之间的差异,甚至可能在运行时)。
  • 您可以捕获 OOM 并尝试按照您的建议(或我的理解)再次分块分配。然后它会再次因 OOM 失败或工作。
  • 如果您正在运行 32 位代码,您可能还需要担心大对象堆碎片。因此,有可能分配两个 0.5Gb 块,其中 1Gb 块失败。如果您需要处理对象,只需启用gcAllowVeryLargeObjects 似乎更合理。也就是说,我希望异常包括它尝试分配的内存量,因为这在调查异常时很有用。

标签: c# exception out-of-memory


【解决方案1】:

我可以解释一下 LinqPad 和 Visual Studio 的区别:

如果您运行以下代码的 x86 DEBUGRELEASE .Net Framework 4.8 构建:

static void Main()
{
    try
    {
        List<Data> a = new List<Data>(134217725);
    }
    
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }

    try
    {
        List<Data[]> b = new List<Data[]>();

        for (int i = 0; i < 13421772; i++)
        {
            b.Add(new Data[134217724]);
        }
    }

    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }
}

对于RELEASE 构建,您将获得:

Array dimensions exceeded supported range.
Exception of type 'System.OutOfMemoryException' was thrown.

对于 DEBUG 构建,您将获得:

Exception of type 'System.OutOfMemoryException' was thrown.
Exception of type 'System.OutOfMemoryException' was thrown.

这意味着 LINQPAD 版本是 RELEASE,Visual Studio 版本是 DEBUG

所以答案是:是的,显然有一些区别,但是:

  • 只有消息不同
  • 我们不应该依赖这种永不改变的方式
  • DEBUGRELEASE 版本之间存在差异。

旁白:

在我的 PC 上,上面测试代码的 DEBUG 构建会立即引发两个 OutOfMemoryException 异常。

但是,RELEASE 构建会很快抛出第一个 OutOfMemoryException,但在它抛出第二个异常之前需要几秒钟。在此期间,它的内存使用量会增加(根据任务管理器)。

很明显,引擎盖下还有其他一些差异,至少对于 .Net Framework 4.8 而言。我没有用 .Net 5 或 .Net Core 尝试过。

【讨论】:

  • 谢谢,我没有想到 DEBUGRELEASE 构建对于同一个异常可能有不同的错误消息。通过切换到 RELEASE,我能够在 VS 中获得不同的错误消息,但值得注意的是,我还必须针对特定平台(不适用于 RELEASE + Any CPU)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 2017-08-04
  • 1970-01-01
  • 2016-10-18
  • 1970-01-01
  • 1970-01-01
  • 2016-04-02
相关资源
最近更新 更多