← Назад к вопросам

Всегда ли сборщик мусора чистит все поколения?

1.7 Middle🔥 201 комментариев
#Память и Garbage Collector

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Всегда ли сборщик мусора чистит все поколения?

Нет, сборщик мусора (.NET Garbage Collector) не всегда чистит все поколения. Это сделано для оптимизации производительности.

Три поколения объектов

.NET использует поколенческую сборку мусора:

  • Generation 0 (Gen 0) — только что созданные объекты
  • Generation 1 (Gen 1) — объекты, пережившие одну сборку
  • Generation 2 (Gen 2) — долгоживущие объекты

Как работает сборка?

// Объекты новые, попадают в Gen 0
var obj1 = new List<int>();
var obj2 = new Dictionary<string, int>();
var obj3 = new User();

// Когда памяти в Gen 0 недостаточно - запускается сборка Gen 0
// Gen 0 очищается от неиспользуемых объектов
// Оставшиеся объекты переходят в Gen 1

// При следующей сборке Gen 0 очищается снова
// Gen 1, если переполнена, очищается и переходит в Gen 2

// Gen 2 очищается реже всего

Эффективность сборок

ПоколениеЧастотаПричина
Gen 0ЧастоМного новых объектов быстро становятся мусором
Gen 1СреднеОбъекты иногда живут дольше
Gen 2РедкоДолгоживущие объекты (Config, Services)

Практический пример

public class MemoryExample
{
    public static void Main()
    {
        Console.WriteLine($"До: Gen 0 = {GC.GetTotalMemory(false)}");

        // Создаём много временных объектов
        for (int i = 0; i < 1000; i++)
        {
            var list = new List<int>();
            for (int j = 0; j < 10000; j++)
            {
                list.Add(j);
            }
            // list выходит из scope и становится мусором
        }

        Console.WriteLine($"После: Gen 0 = {GC.GetTotalMemory(false)}");

        // Явно вызываем сборку мусора
        GC.Collect();  // Собирает все поколения
        GC.WaitForPendingFinalizers();

        Console.WriteLine($"После сборки: Gen 0 = {GC.GetTotalMemory(false)}");
    }
}

Уровни сборки

// Сборка только Gen 0
GC.Collect(0);

// Сборка Gen 0 и Gen 1
GC.Collect(1);

// Сборка всех поколений (Gen 0, 1, 2)
GC.Collect();  // или GC.Collect(2);

// Полная сборка
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Когда сборщик автоматически включается?

  1. При нехватке памяти в поколении
  2. В фоне (в .NET 5+: background GC)
  3. По таймеру (редко)
  4. При явном вызове GC.Collect()

Давление памяти

public class GCPressure
{
    // Низкое давление - объекты быстро удаляются
    public void LowPressure()
    {
        using (var list = new List<int>())
        {
            list.Add(1);
        }  // Сразу удалён, не попадает в Gen 1
    }

    // Высокое давление - много объектов живут долго
    private List<User> _cachedUsers = new();  // Gen 2

    public void HighPressure()
    {
        for (int i = 0; i < 100000; i++)
        {
            _cachedUsers.Add(new User { Id = i });  // Переходит в Gen 2
        }
        // Кэш растёт, требует частых сборок Gen 2
    }
}

Оптимизация сборок

// ❌ ПЛОХО - создаём кучу временных объектов
public string ProcessData(int[] data)
{
    string result = "";
    foreach (var item in data)
    {
        result += item.ToString() + ", ";  // Новая строка каждый раз!
    }
    return result;
}

// ✅ ХОРОШО - используем StringBuilder
public string ProcessData(int[] data)
{
    var sb = new StringBuilder();
    foreach (var item in data)
    {
        sb.Append(item).Append(", ");
    }
    return sb.ToString();
}

Мониторинг сборок

var gen0 = GC.CollectionCount(0);
var gen1 = GC.CollectionCount(1);
var gen2 = GC.CollectionCount(2);

Console.WriteLine($"Gen 0 сборок: {gen0}");
Console.WriteLine($"Gen 1 сборок: {gen1}");
Console.WriteLine($"Gen 2 сборок: {gen2}");

// В ASP.NET Core через метрики
public class GCMetrics
{
    public static void LogGCStats()
    {
        var totalMemory = GC.GetTotalMemory(false);
        var totalMemoryMB = totalMemory / (1024 * 1024);
        Console.WriteLine($"Total Memory: {totalMemoryMB} MB");
    }
}

Важные параметры

// GC.GCCollectionMode - тип сборки
GC.Collect(2, GCCollectionMode.Default);   // Обычная
GC.Collect(2, GCCollectionMode.Forced);    // Принудительная
GC.Collect(2, GCCollectionMode.Optimized); // Оптимизированная (.NET 5+)

// GCSettings - настройки
GCSettings.IsServerGC;         // Серверная ГЦ?
GCSettings.LargeObjectHeapCompactionMode;  // LOH сжатие

В ASP.NET Core

// Background GC (по умолчанию в .NET 5+)
// Работает в отдельном потоке, не блокирует приложение

// Кроме того, есть LOH (Large Object Heap) - для объектов > 85KB
var bigArray = new byte[1000000];  // В LOH

// LOH не сжимается автоматически, нужна явная сборка
GCLargeObjectHeapCompactionMode.CompactOnce;

Практическое значение

Не полагайся на сборку мусора:

  • Используй using/try-finally для критичных ресурсов
  • Вызывай Dispose() явно при необходимости
  • Избегай создания огромного количества временных объектов

Но и не паникуй:

  • GC отлично оптимизирован
  • Обычно всё работает автоматически
  • Явный GC.Collect() вызывай редко (замораживает приложение)

Итого

Сборщик мусора не всегда чистит все поколения:

  • Gen 0 чистится часто (много новых объектов)
  • Gen 1 чистится реже (объекты пережили одну сборку)
  • Gen 2 чистится редко (долгоживущие объекты)

Это сделано для оптимизации производительности - молодые объекты быстрее удаляются, а долгоживущие проверяются реже.

Всегда ли сборщик мусора чистит все поколения? | PrepBro