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

Как работает сборщик мусора в Unity?

1.8 Middle🔥 161 комментариев
#Unity Core#Оптимизация#Управление памятью

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

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

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

Как работает сборщик мусора (Garbage Collector) в Unity

Сборщик мусора (Garbage Collector, GC) в Unity — это автоматизированная система управления памятью, основанная на Boehm-Demers-Weiser garbage collector. Её ключевая задача — освобождать память, занятую объектами, которые больше не используются приложением, предотвращая утечки памяти. Однако в реальном времени, особенно в играх, её работа может вызывать просадки производительности (GC spikes), что требует глубокого понимания и контроля со стороны разработчика.

Основные принципы работы

  1. Управляемая куча (Managed Heap): Unity использует среду выполнения .NET (Mono или IL2CPP), где объекты ссылочных типов (например, экземпляры классов, строки, массивы) создаются в области памяти, называемой управляемой кучей.
  2. Ссылки и достижимость: GC периодически сканирует кучу, начиная с корневых объектов (глобальные и статические переменные, активные локальные переменные в стеке вызовов, ссылки в регистрах CPU). Любой объект, до которого можно добраться через цепочку ссылок от корня, считается "живым" (in use). Объекты, недостижимые из корней, помечаются как "мусор" (garbage).
  3. Цикл сборки мусора: Процесс делится на две основные фазы:
    *   **Маркировка (Mark)**: Обход графа ссылок и пометка всех достижимых объектов.
    *   **Очистка (Sweep)**: Освобождение памяти, занятой неотмеченными (недостижимыми) объектами. При использовании **сборщика Boehm** память не уплотняется, что может приводить к **фрагментации кучи**.

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

Главная проблема в Unity — stop-the-world пауза. На время работы GC (особенно фазы маркировки) основной поток выполнения приложения приостанавливается. Длительность паузы прямо пропорциональна количеству живых объектов в куче, а не количеству собираемого мусора.

// Пример кода, активно генерирующего аллокации и, как следствие, мусор:
void Update() {
    // ПЛОХО: Каждый кадр создается новый массив -> аллокация на куче -> мусор.
    string[] enemyNames = new string[] { "Orc", "Goblin", "Troll" };
    ProcessEnemies(enemyNames);

    // ПЛОХО: Строковые операции часто создают новые объекты.
    string status = "Health: " + currentHealth + "/" + maxHealth;
    UpdateUI(status);
}

Ключевые стратегии оптимизации

Чтобы минимизировать влияние GC, необходимо снижать количество аллокаций на управляемой куче во время выполнения игры (в Update(), FixedUpdate(), часто вызываемых методах).

  • Пулы объектов (Object Pooling): Вместо постоянного создания и уничтожения объектов (пули, враги, эффекты) их заранее создают в пуле и переиспользуют.
    // Упрощенный пример пула объектов
    public class ProjectilePool {
        private Queue<Projectile> pool = new Queue<Projectile>();
        private Projectile prefab;
    
        public Projectile GetProjectile() {
            if (pool.Count > 0) {
                return pool.Dequeue().Activate();
            }
            return Instantiate(prefab); // Аллокация только при необходимости.
        }
    
        public void ReturnProjectile(Projectile proj) {
            proj.Deactivate();
            pool.Enqueue(proj); // Возврат в пул, а не Destroy.
        }
    }
    
  • Кэширование ссылок: Избегайте повторяющихся вызовов, возвращающих новые данные (например, GetComponent<>() в Update()).
    private Rigidbody rb;
    
    void Start() {
        rb = GetComponent<Rigidbody>(); // Однократная аллокация.
    }
    
    void Update() {
        // ХОРОШО: Используем кэшированную ссылку, нет аллокаций.
        rb.AddForce(Vector3.up * thrust);
    }
    
  • Использование структур (struct): Значимые типы размещаются в стеке (если не являются частью класса) и не нагружают кучу. Но важно помнить о накладных расходах при копировании больших структур.
  • Повторное использование коллекций: Методы Clear() для List<T> или Dictionary<K,V> очищают данные, но не освобождают выделенную под буфер память, позволяя использовать её повторно без новых аллокаций.
  • Строковые операции: Используйте StringBuilder для сложной конкатенации строк, избегайте string.Format() или сложных ToString() в часто вызываемом коде.

Инструменты для анализа

  • Unity Profiler (Memory Area): Ключевой инструмент. Позволяет отслеживать график выделения памяти на куче (GC Allocated), видеть вызовы сборщика и анализировать объекты, создающие аллокации.
  • Deep Profiling: Включается в Profiler для детального отслеживания всех вызовов методов.
  • Unity Heap Explorer (пакет): Позволяет детально исследовать снимок (snapshot) управляемой кучи, находить удерживающие ссылки и выявлять причины утечек.

Вывод: Сборщик мусора в Unity — это "палка о двух концах". Он избавляет разработчика от ручного управления памятью, но требует осознанного подхода к написанию кода. Для создания плавного игрового процесса критически важно минимизировать аллокации во время выполнения, используя пулы, кэширование и выбирая правильные типы данных. Регулярный анализ через Profiler является неотъемлемой частью рабочего процесса профессионального Unity-разработчика.