Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 捕获OutOfMemoryException是如何工作的?_C#_.net_Clr_Out Of Memory - Fatal编程技术网

C# 捕获OutOfMemoryException是如何工作的?

C# 捕获OutOfMemoryException是如何工作的?,c#,.net,clr,out-of-memory,C#,.net,Clr,Out Of Memory,我有点困惑,我们可以使用try/catch块捕获OutOfMemoryException 给定以下代码: Console.WriteLine("Starting"); for (int i = 0; i < 10; i++) { try { OutOfMemory(); } catch (Exception exception) { Console.WriteLine(exception.ToString());

我有点困惑,我们可以使用try/catch块捕获
OutOfMemoryException

给定以下代码:

Console.WriteLine("Starting");

for (int i = 0; i < 10; i++)
{
    try
    {
        OutOfMemory();
    }
    catch (Exception exception)
    {
        Console.WriteLine(exception.ToString());
    } 
}

try
{
    StackOverflow();
}
catch (Exception exception)
{
    Console.WriteLine(exception.ToString());
}

Console.WriteLine("Done");
Console.WriteLine(“启动”);
对于(int i=0;i<10;i++)
{
尝试
{
OutOfMemory();
}
捕获(异常)
{
Console.WriteLine(exception.ToString());
} 
}
尝试
{
堆栈溢出();
}
捕获(异常)
{
Console.WriteLine(exception.ToString());
}
控制台。写入线(“完成”);
我用于创建OutOfMemory+StackOverflowException的方法:

public static void OutOfMemory()
{
    List<byte[]> data = new List<byte[]>(1500);

    while (true)
    {
        byte[] buffer = new byte[int.MaxValue / 2];

        for (int i = 0; i < buffer.Length; i++)
        {
            buffer[i] = 255;
        }

        data.Add(buffer);
    }
}

static void StackOverflow()
{
    StackOverflow();
}
publicstaticvoidoutofmemory()
{
列表数据=新列表(1500);
while(true)
{
字节[]缓冲区=新字节[int.MaxValue/2];
for(int i=0;i
它打印出
OutOfMemoryException
10次,然后由于无法处理的
StackOverflowException
而终止

执行程序时,RAM图如下所示:


我现在的问题是,为什么我们能够捕获
OutOfMemoryException
?捕获它之后,我们可以继续执行我们想要的任何代码。正如RAM图所证明的,内存被释放了。运行时如何知道哪些对象可以GC,哪些对象仍然需要进一步执行?

很可能会抛出OutOfMemoryException,因为您正在运行一个32位程序,内存图中没有指示系统有多少ram,所以可能尝试将其构建为64位,也许可以用来防止这种情况发生

您还可以让我们知道OutOfMemory()函数中的内容,以便更清楚地了解情况

p.S.StackOverFlow是唯一无法处理的错误

编辑:如上所述,我认为这是合乎逻辑的,因此在前面没有提到。例如,如果您试图分配的内存超过“空闲”内存,则无法分配,并且会发生异常。当您使用data.Add()分配大型数组时,它会在最终的“非法”添加发生之前终止,因此仍然有可用内存

所以我假设它在这一点上是data.Add(buffer);在构建阵列的过程中,当您通过向“数据”添加一个400MB字节的阵列而超出2GB进程限制时,就会出现此问题,例如,一个包含约10亿个对象的阵列,一块4字节,我预计大约为400MB


在.net 4.5最大进程内存分配为2GB之前,4.5更大的进程内存分配可用。

不确定这是否回答了您的问题,但关于它如何决定要清理哪些对象的(简化)解释如下:

垃圾收集器接收程序中的每个正在运行的线程并标记所有顶级对象,这意味着可以从堆栈帧访问的所有对象(即,在当前执行点由局部变量指向的所有对象)以及由静态字段指向的所有对象

然后,它标记下一级对象,这意味着由先前标记的对象的所有字段指向的所有对象。重复此步骤,直到没有标记新对象

因为C#不允许在正常上下文中使用指针,所以一旦完成上一步,就可以保证后续代码无法访问未标记的对象,因此可以安全地清理这些对象


在您的情况下,如果分配给内存管理器的对象没有通过引用保留,这意味着GC将有机会清理它们。另外,请记住OutOfMemoryException指的是CLR程序的托管内存,而GC的工作稍微超出了这个“框”。

您可以捕获OutOfMemoryException的原因是语言设计者决定让您使用它。这有时(但通常不)实用的原因是,在某些情况下,这是一种可恢复的情况


如果您试图分配一个巨大的数组,可能会出现OutOfMemoryException,但该巨大数组的内存实际上尚未分配,因此其他代码仍然可以正常运行。此外,异常导致的堆栈展开可能会导致其他对象符合垃圾收集的条件,从而进一步增加可用内存量。

GC对程序中使用的引用进行分析,并可以丢弃任何未在任何地方使用的对象

OutOfMemoryException
并不意味着内存完全耗尽,它只是意味着内存分配失败。如果您试图一次分配一个大的内存区域,可能仍然有大量的可用内存

当没有足够的可用内存进行分配时,系统会执行垃圾收集以尝试释放内存。如果仍然没有足够的内存进行分配,它将抛出异常

无法处理
StackOverflowException
,因为这意味着堆栈已满,并且无法像处理堆一样从堆栈中删除任何内容。您将需要更多的堆栈空间来继续运行处理异常的代码,但没有更多的堆栈空间。

您的
OutOfMemory()
方法创建了该方法作用域的本地数据结构(列表)。当您的执行线程位于
OutOfMemory
方法中时,当前堆栈帧被视为列表的GC根。一旦线程在catch块中结束,堆栈框架就会弹出,列表实际上变得不可访问。因此,垃圾收集器确定它可以安全地收集列表(正如您在内存图中所观察到的那样)。

显示OutOfMemory()方法。我打赌你没有保留对它的记忆的引用