实际 OOM 和 2GB 对象 OOM 之间的区别?

Difference between actual OOM and 2GB object OOM?

我想知道为实际 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 中的代码,但出现以下异常:

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)
System.OutOfMemoryException
  HResult=0x8007000E
  Message=Exception of type 'System.OutOfMemoryException' was thrown.
  Source=ConsoleApp1
  StackTrace:
   at ConsoleApp1.Program.Main(String[] args)

从这里看来,这两个异常之间似乎没有显着差异(除了堆栈 trace/source)。

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

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

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

我可以解释一下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 版本不同。

旁白:

在我的电脑上,上述测试代码的 DEBUG 构建立即抛出两个 OutOfMemoryException 异常。

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

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