Что такое GC (garbadge collector/сборщик мусора)?go-62

Что такое сборщик мусора?

Сборщик мусора (GC) — это механизм автоматического управления памятью, который:

  • Освобождает неиспользуемые объекты в куче (heap)
  • Предотвращает утечки памяти
  • Избавляет разработчика от ручного управления памятью

Основные характеристики GC в Go

1. Неблокирующий

  • Большая часть работы выполняется параллельно с программой
  • Короткие остановки (STW - Stop The World) только на финальных этапах

2. Трехцветный маркировочный алгоритм

  • Использует граф объектов с тремя состояниями:
    • Белый — непросмотренный объект
    • Серый — обрабатываемый объект
    • Черный — обработанный объект

3. Поколенческий с оговорками

  • Хотя официально не заявлен как поколенческий, учитывает "возраст" объектов

Как работает GC в Go?

Фазы работы:

  1. Mark Setup (STW) - подготовка, остановка программы (```10-100µs)
  2. Concurrent Mark - параллельная маркировка используемых объектов
  3. Mark Termination (STW) - завершение маркировки (```50-200µs)
  4. Sweep - освобождение немаркированных объектов (параллельно)
┌─────────────┐    ┌─────────────────┐    ┌─────────────┐    ┌─────────────┐
│ Mark Setup  │───>│ Concurrent Mark │───>│ Mark Term.  │───>│   Sweep     │
└─────────────┘    └─────────────────┘    └─────────────┘    └─────────────┘
     STW                  Concurrent           STW               Concurrent

Алгоритм маркировки:

  1. Начинается с корневых объектов (глобальные переменные, стек и регистры)
  2. Рекурсивно проходит все достижимые объекты
  3. Помечает достижимые объекты как "используемые"

Управление GC

Настройки через окружение:

  1. GOGC - процент роста кучи для запуска GC (по умолчанию 100)

    • GOGC=50 - запускать GC при 50% роста
    • GOGC=off - полностью отключить GC
  2. Мониторинг:

func printGCStats() {
    t := time.NewTicker(time.Second)
    defer t.Stop()

    for range t.C {
        var m runtime.MemStats
        runtime.ReadMemStats(&m)
        fmt.Printf("GC cycles: %d, Last GC: %v\n",
            m.NumGC, time.Unix(0, int64(m.LastGC)))
    }
}

Оптимизации GC в Go

1. Write Barrier

Механизм, который:

  • Отслеживает изменения указателей во время маркировки
  • Гарантирует корректность при конкурентной работе
// Пример, где write barrier важен
func updatePointer(old *Object, new *Object) {
    *old = *new // Write barrier отследит это изменение
}

2. Триггеры сбора мусора

GC запускается когда:

  • Выделено определенное количество памяти (контролируется GOGC)
  • Вызвана runtime.GC() вручную
  • Не хватает памяти для новых аллокаций

Влияние на производительность

Метрики GC:

  • Паузы (STW): обычно < 1ms (Go 1.14+)
  • Процент CPU: 1-5% для большинства приложений
  • Частота: зависит от скорости аллокации памяти

Как уменьшить влияние GC:

  1. Уменьшайте аллокации в куче:

    • Используйте sync.Pool для часто создаваемых объектов
    • Предпочитайте stack-аллокации (value types)
  2. Пример оптимизации:

// Плохо: создает новый объект при каждом вызове
func getData() *Data {
    return &Data{} // Аллокация в куче
}

// Лучше: использовать sync.Pool
var dataPool = sync.Pool{
    New: func() interface{} { return &Data{} },
}

func getData() *Data {
    return dataPool.Get().(*Data)
}

Эволюция GC в Go

Версия Основные улучшения
1.0 Простой mark-and-sweep
1.5 Конкурентный маркировщик
1.8 Уменьшение STW до <1ms
1.12 Оптимизация работы с большими кучами
1.14 Улучшение планирования GC
1.18 Оптимизация работы с множеством мелких объектов

Резюмируем

сборщик мусора в Go — это высокооптимизированная система, обеспечивающая баланс между производительностью и простотой разработки. Понимание его работы помогает писать эффективный код, минимизирующий накладные расходы на управление памятью. Современные версии Go делают GC практически "незаметным" для большинства приложений, сохраняя при этом предсказуемое поведение.