C# 捕获OutOfMemoryException是如何工作的?
我有点困惑,我们可以使用try/catch块捕获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());
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()方法。我打赌你没有保留对它的记忆的引用