Я немного смущен тем, что мы можем просто поймать OutOfMemoryException используя блок try/catch.
Учитывая следующий код:
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");
Методы, которые я использовал для создания 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();
}
Он печатает OutOfMemoryException 10 раз, а затем завершается из-за OutOfMemoryException StackOverflowException, с которым он не может справиться.
График RAM выглядит так, как при выполнении программы: 
Мой вопрос теперь, почему мы можем поймать OutOfMemoryException? После этого мы можем просто выполнить любой код, который мы хотим. Как доказано графиком ОЗУ, выпущена память. Как среда выполнения знает, какие объекты она может GC и которые все еще необходимы для дальнейшего выполнения?