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

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

2.0 Middle🔥 292 комментариев
#Оптимизация#Управление памятью

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

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

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

Обзор сборщика мусора (Garbage Collector) в Unity

Сборщик мусора (GC) — это часть системы управления памятью в .NET/Mono, которая автоматически освобождает память, занятую объектами, ставшими недоступными для программы. В Unity используется GC из среды выполнения Mono (или .NET в более новых версиях), и его понимание критически важно для оптимизации производительности.

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

GC работает по следующему циклу:

  1. Выделение памяти: Когда создается новый объект (например, через new или Instantiate), память выделяется в Managed Heap (управляемой куче).
  2. Отслеживание ссылок: GC отслеживает все живые ссылки на объекты в куче.
  3. Определение мусора: Объект считается "мусором" (garbage), если на него нет активных ссылок из "корневых" мест (например, статических полей, локальных переменных в стеке активных методов, полей других живых объектов).
  4. Сборка и освобождение: Периодически GC выполняет сборку:
    *   Обнаруживает все недоступные объекты.
    *   Освобождает их память (на некоторых платформах память возвращается системе, на других просто помечается как свободная внутри кучи).
    *   Для уменьшения фрагментации часто выполняет **компактизацию** (compaction) — перемещение живых объектов в начало кучи, чтобы свободная память была непрерывным блоком.

Ключевые типы сборок в Unity

В Unity существуют два основных типа сборки мусора, которые влияют на производительность:

  • Малые сборки (Small/Incremental GC): Происходят часто и быстро, обрабатывая короткоживущие объекты. Могут вызывать микро-задержки.
  • Полные сборки (Full GC): Происходят при заполнении управляемой кучи или по специальному запросу. Они затрагивают весь граф объектов, выполняют компактизацию и вызывают заметные фризы (падения FPS) — это одна из главных проблем для оптимизации.

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

Полная сборка мусора — основной источник проблем. Она может блокировать основной поток на десятки или сотни миллисекунд, что катастрофично для стабильного FPS.

Основные стратегии минимизации воздействия GC:

  1. Сокращение объема выделений:
    *   Избегайте частого создания временных объектов внутри циклов (например, в `Update()`).
    *   Используйте пулы объектов (**Object Pooling**) для часто создаваемых/уничтожаемых объектов (пули, эффекты, враги).

// Пример простого пула для GameObject
public class ObjectPool : MonoBehaviour
{
    private Queue<GameObject> pooledObjects = new Queue<GameObject>();
    public GameObject Prefab;

    public GameObject GetObject()
    {
        if (pooledObjects.Count > 0)
        {
            GameObject obj = pooledObjects.Dequeue();
            obj.SetActive(true);
            return obj;
        }
        return Instantiate(Prefab);
    }

    public void ReturnObject(GameObject obj)
    {
        obj.SetActive(false);
        pooledObjects.Enqueue(obj);
    }
}
  1. Контроль над частотой сборок:
    *   Можно явно вызывать сборку в "безопасных" моментах (например, между уровнями) через `GC.Collect()`, но это требует осторожности.
    *   В новых версиях Unity доступен **Incremental Garbage Collector**, который разбивает работу полной сборки на мелкие шаги между кадрами, значительно уменьшая фризы. Его рекомендуется включать для тяжелых проектов.

  1. Особенности Unity:
    *   Операции, которые кажутся "невинными", могут вызывать выделения в управляемой куче: некоторые операции с **строками** (конкатенация, методы `ToString()`), **boxing** (преобразование структур в объекты), использование **делегатов** в цикле, неправильное использование **LINQ** (которое часто создает временные коллекции).
    *   Следите за выделениями в **Profiler** (окно "Deep Profiling" и раздел "Memory").

// Плохо: создает новую строку каждый кадр
void Update()
{
    string label = "Score: " + currentScore; // Выделение памяти!
}

// Лучше: использовать StringBuilder или заранее подготовленный формат
private StringBuilder sb = new StringBuilder();
void Update()
{
    sb.Clear();
    sb.Append("Score: ");
    sb.Append(currentScore);
    // Использовать sb.ToString() только при необходимости отображения
}

Дополнительные технологии

Для проектов, где GC является критической проблемой (например, мобильные VR/AR), можно рассмотреть более радикальные решения:

  • Использование неуправляемых (unmanaged) коллекций через Unity.Collections (например, NativeArray) — они полностью избегают контроля GC.
  • Переход на Burst Compiler для вычислений — также часто использует NativeArray и обходит GC.
  • Профилирование и анализ с помощью Memory Profiler для поиска конкретных источников выделений.

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