Ответ
В .NET управляемая куча (Managed Heap) организована в поколения (Generations) для оптимизации работы сборщика мусора (Garbage Collector, GC). Эта модель основана на эмпирическом наблюдении: чем моложе объект, тем короче его время жизни.
Три основных поколения:
-
Generation 0 (Gen 0)
- Назначение: Здесь размещаются все новые объекты малого размера.
- Особенности: Самая маленькая по размеру область. Сборка мусора (GC) здесь происходит чаще всего и является самой быстрой, так как проверяется только эта область.
- Почему это эффективно: Большинство временных объектов (например, локальные переменные в методе) умирают здесь, что позволяет быстро освобождать память.
-
Generation 1 (Gen 1)
- Назначение: Буферная зона. Сюда перемещаются объекты, которые пережили сборку в Gen 0.
- Особенности: Имеет промежуточный размер. Сборка здесь происходит реже, чем в Gen 0, но чаще, чем в Gen 2.
-
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.