Какие разделы (поколения) есть в управляемой куче (.NET)?

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

Ответ

В .NET управляемая куча (Managed Heap) организована в поколения (Generations) для оптимизации работы сборщика мусора (Garbage Collector, GC). Эта модель основана на эмпирическом наблюдении: чем моложе объект, тем короче его время жизни.

Три основных поколения:

  1. Generation 0 (Gen 0)

    • Назначение: Здесь размещаются все новые объекты малого размера.
    • Особенности: Самая маленькая по размеру область. Сборка мусора (GC) здесь происходит чаще всего и является самой быстрой, так как проверяется только эта область.
    • Почему это эффективно: Большинство временных объектов (например, локальные переменные в методе) умирают здесь, что позволяет быстро освобождать память.
  2. Generation 1 (Gen 1)

    • Назначение: Буферная зона. Сюда перемещаются объекты, которые пережили сборку в Gen 0.
    • Особенности: Имеет промежуточный размер. Сборка здесь происходит реже, чем в Gen 0, но чаще, чем в Gen 2.
  3. Generation 2 (Gen 2)

    • Назначение: Здесь находятся долгоживущие объекты, пережившие несколько сборок мусора (например, статические поля, кэши, объекты уровня приложения).
    • Особенности: Самая большая по размеру область. Полная сборка мусора (Gen 2) — это наиболее ресурсоемкая операция, которая может вызывать заметные паузы в работе приложения.

Large Object Heap (LOH) — Куча больших объектов:

  • Назначение: Отдельная область для объектов, размер которых превышает 85 000 байт (например, большие массивы byte[] или string).
  • Особенности: Объекты попадают сюда сразу при создании. LOH собирается только во время полной сборки (Gen 2). Из-за отсутствия компактизации (перемещения объектов для устранения фрагментации) может приводить к фрагментации памяти.

Визуализация потока объекта: Новый объект (< 85 KB) → Gen 0 → (Выжил) → Gen 1 → (Выжил) → Gen 2 Новый объект (>= 85 KB) → Large Object Heap (считается частью Gen 2)

Пример на C#:

// Создаем объекты
var shortLived = new byte[500]; // Маленький объект -> Gen 0
var longLived = new List<string>(); // Объект-контейнер -> Gen 0
var hugeArray = new byte[100_000]; // Большой объект -> Large Object Heap (LOH)

// Симулируем работу: shortLived скоро станет мусором, longLived останется.
longLived.Add("some data");

// Принудительная сборка мусора только в Gen 0 и Gen 1.
// Это демонстрация, в реальном коде GC.Collect() вызывать обычно не нужно.
GC.Collect(1); // Параметр 1 означает сборку в Gen 0 и Gen 1.
// После этой операции, если longLived все еще используется, он переместится в Gen 2.