Какие есть способы вызвать исключение OutOfMemoryException в C#?

«Какие есть способы вызвать исключение OutOfMemoryException в C#?» — вопрос из категории Управление памятью, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Исключение OutOfMemoryException возникает, когда среде выполнения CLR не удается выделить новую память в управляемой куче. Вот типичные сценарии, которые могут к этому привести:

1. Непрерывное выделение больших массивов или объектов

Самый прямой способ — быстрое и неограниченное создание объектов, с которым сборщик мусора (GC) не успевает справиться.

// Пример 1: Создание больших массивов
List<byte[]> memoryHog = new List<byte[]>();
try
{
    while (true)
    {
        // Выделяем блок по 100 МБ
        memoryHog.Add(new byte[100 * 1024 * 1024]);
    }
}
catch (OutOfMemoryException ex)
{
    Console.WriteLine($"Out of Memory: {ex.Message}");
}

2. Утечки памяти через долгоживущие ссылки

Объекты, на которые сохраняются ссылки, никогда не собираются GC. Частая причина — статические коллекции.

public static class MemoryLeak
{
    // Статическая коллекция живет всё время работы домена приложения
    private static List<byte[]> _globalCache = new List<byte[]>();

    public static void CauseLeak()
    {
        // Каждый добавленный массив никогда не будет удален
        for (int i = 0; i < 10000; i++)
        {
            _globalCache.Add(new byte[10 * 1024 * 1024]); // 10 МБ
        }
    }
}

3. Фрагментация Large Object Heap (LOH)

Объекты размером >= 85 000 байт размещаются в LOH. LOH не компактифицируется по умолчанию (до .NET 4.5.1), что может привести к фрагментации и невозможности найти непрерывный блок памяти для нового большого объекта, даже если свободной памяти в сумме много.

// Чередование больших и маленьких объектов может фрагментировать LOH
var largeObjects = new List<byte[]>();
for (int i = 0; i < 1000; i++)
{
    // Создаем большой объект
    largeObjects.Add(new byte[85000]);
    // Создаем много маленьких объектов, которые умирают и оставляют "дыры"
    var temp = new byte[100];
}
// В какой-то момент выделение нового большого массива может не удасться
byte[] failedAllocation = new byte[85000]; // Возможен OutOfMemoryException

4. Ограничения процесса и среды

  • 32-битные приложения: Имеют ограничение адресного пространства в ~2 ГБ (или ~3-4 ГБ с флагом LARGEADDRESSAWARE).
  • 64-битные приложения: Теоретически ограничены объемом физической памяти и файла подкачки, но на практике могут столкнуться с ограничениями среды выполнения или фрагментацией.

Важное отличие: OutOfMemoryException относится к куче (heap). StackOverflowException — это отдельное исключение, связанное с переполнением стека вызовов (stack), обычно из-за бесконечной или слишком глубокой рекурсии.