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

В чём разница между Garbage Collector и Incremental Garbage Collector?

3.0 Senior🔥 131 комментариев
#Оптимизация#Управление памятью

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Разница между Garbage Collector и Incremental Garbage Collector

Основное различие между классическим Garbage Collector (GC, сборщик мусора) и Incremental Garbage Collector (инкрементальный GC) заключается в стратегии выполнения: традиционный GC выполняет сборку мусора за один непрерывный "стоп-мир" (stop-the-world) этап, который может вызывать заметные фризы (просадки FPS), в то время как инкрементальный GC разбивает эту работу на множество маленьких этапов, выполняемых между кадрами игры, что значительно снижает воздействие на производительность и плавность геймплея. В контексте Unity это критически важно, поскольку движок исторически страдал от проблем с GC-фризами, особенно на платформах с ограниченными ресурсами (мобильные устройства, VR).

Классический Garbage Collector в Unity

До версии 2019.3 Unity использовала Boehm-Demers-Weiser garbage collector для управляемого кода (C#). Его работа выглядела так:

  • Монофазный сбор: При запуске GC приложение полностью останавливается (stop-the-world).
  • Mark and Sweep: Алгоритм проходит по всем "живым" объектам, начиная с корневых ссылок (статические поля, локальные переменные в стеке, регистры CPU), и помечает их. Все непомеченные объекты считаются мусором и удаляются в фазе очистки (sweep).
  • Проблема: Если управляемая куча (managed heap) большая (содержит много объектов), процесс маркировки и очистки может занимать десятки миллисекунд. В игре, работающей на 60 FPS (где на кадр отводится всего ~16.6 мс), это приводит к явному "зависанию" или подёргиванию изображения.
// Пример кода, который может спровоцировать большой GC-аллокейшн и фриз
void Update() {
    // Создание новой строки в каждом кадре -> аллокация в куче
    string debugText = "Frame: " + Time.frameCount;
    // Использование debugText...
    // В конце кадра строка становится мусором.
    // Когда куча заполнится, запустится Stop-the-World GC.
}

Incremental Garbage Collector (Incremental GC)

Начиная с версии Unity 2019.3, был введён Incremental Garbage Collector как усовершенствованная альтернатива (а позже — как настройка по умолчанию). Его ключевая идея — инкрементальность.

  • Разделение работы: Тяжёлая фаза маркировки (marking) разбивается на множество мелких пакетов работ.
  • Работа между кадрами: Unity выполняет небольшую порцию работы по маркировке в промежутках между кадрами, обычно в конце одного кадра или начале следующего, в течение выделенного временного интервала (например, 2-5 мс).
  • Отсутствие длительных фризов: Поскольку работа растянута во времени, вместо одного длительного 50-миллисекундного фриза вы получаете, например, 10 едва заметных микро-фризов по 5 мс каждый, что гораздо меньше влияет на воспринимаемую плавность.

Важные технические детали Incremental GC в Unity:

  • Не инкрементальная очистка: Фаза очистки (sweep) и сжатия (compaction, если включено) по-прежнему выполняются как единый стоп-мир этап. Однако они обычно значительно быстрее фазы маркировки.
  • Влияние на производительность CPU: Инкрементальный GC добавляет небольшие постоянные накладные расходы на CPU (постоянная работа по маркировке), но исключает катастрофические пиковые нагрузки. Это trade-off в пользу стабильности.
  • Требует настройки: Для эффективной работы нужно корректно настроить интервал работы GC (время, выделяемое на инкрементальную маркировку за кадр) через GarbageCollector.incrementalTimeSliceNanoseconds.
// Пример настройки Incremental GC через скрипт (обычно делается один раз при запуске)
using UnityEngine;
using UnityEngine.Scripting;

public class GCConfigurator : MonoBehaviour {
    void Start() {
        // Установка целевого времени работы инкрементального GC за кадр (например, 1 миллисекунда).
        // Значение в наносекундах: 1 мс = 1 000 000 нс.
        GarbageCollector.incrementalTimeSliceNanoseconds = 1000000;

        // Включение инкрементационного режима (по умолчанию включён в новых версиях).
        // GarbageCollector.incrementalModeEnabled = true;
    }
}

Практические выводы для Unity-разработчика

  • Цель одна, методы разные: Оба сборщика преследуют одну цель — автоматическое освобождение памяти от неиспользуемых управляемых объектов (в C#). Разница — в алгоритмической реализации, влияющей на производительность.
  • Incremental GC — не панацея: Он маскирует проблему, а не решает её. Если ваш код генерирует гигабайты мусора в секунду, инкрементальный GC не успеет его убирать, и стоп-мир этапы или постоянные микропаузы станут неизбежны. Оптимизация аллокаций (пулинг объектов, кэширование, отказ от генерации мусора в Update()) остаётся первостепенной задачей.
  • Выбор в настройках проекта: В Player Settings > Other Settings > Configuration можно выбрать тип сборщика: Incremental (рекомендуется), Boehm GC (устаревший) или Disabled (только для продвинутых сценариев, например, с Unity.Collections и ручным управлением).
  • Мониторинг: Используйте Unity Profiler (окно Memory) или Deep Profiling для отслеживания активности GC, размера кучи и того, какие вызовы методов вызывают аллокации.

Заключение: Переход с классического на инкрементальный сборщик мусора в Unity стал важнейшим шагом в улучшении пользовательского опыта, особенно для платформ, чувствительных к задержкам. Однако понимание принципа его работы лишь подчёркивает старую истину: самый эффективный сборщик мусора — тот, который не должен запускаться слишком часто, благодаря грамотно написанному коду разработчика.