Unreal EngineSeniorTechnical

Что такое сборщик мусора (garbage collector) в Unreal Engine и как предотвратить уничтожение объектов?

GC в UE периодически обходит граф UObject-ссылок и уничтожает недостижимые объекты; чтобы объект жил, достаточно иметь UPROPERTY-ссылку на него или вызвать AddToRoot(). Сырые указатели UObject* без UPROPERTY — невидимы GC.

Как работает Garbage Collector в Unreal Engine

UE использует трассирующий GC (Mark & Sweep). Раз в несколько секунд (интервал задаётся gc.TimeBetweenPurgingPendingKillObjects) движок обходит все зарегистрированные UObject и помечает достижимые. Объекты без живых ссылок помечаются как PendingKill, а затем физически удаляются в следующем цикле очистки.

Что считается «живой ссылкой»

  • UPROPERTY-поле в UObject — главный способ.
  • Вызов AddToRoot() — добавляет объект в корневой набор, GC никогда его не тронет.
  • Глобальный массив TArray<UObject*> с UPROPERTY.
  • Ownership-иерархия Actor: дочерние компоненты держит Outer.

Что GC не видит

  • Сырой UObject* в обычном поле (без UPROPERTY).
  • std::shared_ptr<UObject*> — UE не знает о STL-умных указателях для UObject.
  • Указатель в локальной переменной функции (стек GC не сканирует).

Инструменты защиты объектов от GC

1. UPROPERTY

UCLASS()
class AMyActor : public AActor
{
    GENERATED_BODY()
    UPROPERTY() // без этого макроса MyObject может быть уничтожен GC
    UMyObject* MyObject;
};

2. AddToRoot / RemoveFromRoot

UMySubsystem* Subsystem = NewObject<UMySubsystem>();
Subsystem->AddToRoot(); // объект в корневом наборе — GC не трогает
// ... при завершении работы:
Subsystem->RemoveFromRoot();

3. FGCObjectScopeGuard и FGCObject

// Если класс не является UObject, но держит ссылку
class FMyNativeClass : public FGCObject
{
public:
    UTexture2D* CachedTexture = nullptr;

    virtual void AddReferencedObjects(FReferenceCollector& Collector) override
    {
        Collector.AddReferencedObject(CachedTexture);
    }
    virtual FString GetReferencerName() const override
    {
        return TEXT("FMyNativeClass");
    }
};

4. TStrongObjectPtr (UE 5.x)

#include "UObject/StrongObjectPtr.h"

TStrongObjectPtr<UMyObject> StrongRef(NewObject<UMyObject>());
// Объект жив пока StrongRef существует

Проверка валидности перед использованием

// IsValid() проверяет != nullptr && !IsPendingKill()
if (IsValid(MyActorPtr))
{
    MyActorPtr->DoSomething();
}
// TWeakObjectPtr — не продлевает жизнь, но безопасен для проверки
TWeakObjectPtr<AActor> WeakRef = SomeActor;
if (WeakRef.IsValid())
{
    WeakRef->DoSomething();
}

Консольные переменные для диагностики

gc.TimeBetweenPurgingPendingKillObjects 60  // интервал очистки (сек)
gc.MaxObjectsNotConsideredByGC 1             // объектов в «быстром пути»
obj gc                                       // принудительный GC в консоли
obj refs ClassName=MyActor                  // показать все ссылки на объект

Подводные камни

  • Сырой указатель без UPROPERTY: если забыть макрос — объект уничтожается GC, указатель становится висячим; IsValid() не спасёт (оно вернёт false только если объект стал PendingKill, но не при произвольном висячем указателе).
  • AddToRoot без RemoveFromRoot: утечка памяти — объект никогда не будет освобождён.
  • GC в многопоточном коде: GC может сработать между потоками; нельзя держать UObject* в рабочем потоке без FGCObject.
  • Использование после Destroy(): AActor::Destroy() помечает актора как PendingKill немедленно; после вызова нельзя обращаться к его методам — используйте IsValid().
  • Удержание ссылки на SubObject через внешний массив без UPROPERTY: субобъекты (компоненты, созданные через CreateDefaultSubobject) защищены Outer, но произвольные NewObject без владельца-UObject нуждаются в явной ссылке.
  • Стресс-тест GC: команда obj gc в консоли принудительно запускает GC — полезна для поиска утечек в Development-билде.

Common mistakes

  • Объяснять Unreal garbage collector только по синтаксису, без жизненного цикла и стоимости.
  • Игнорировать ошибки, null/empty состояния, порядок инициализации или режим сборки.
  • Давать пример, который работает в демо, но ломается при изменении владельца ресурса.
  • Использовать макросы или specifier-и наугад и забывать про UObject GC.

What the interviewer is testing

  • Кандидат формулирует точную модель для Unreal garbage collector, а не только определение.
  • Пример компилируем, безопасен по lifetime и соответствует версии технологии.
  • Названы trade-off, ограничения и диагностируемые симптомы ошибки.
  • Понимает границу между C++ кодом, runtime/framework metadata и editor/UI слоем.

Sources

Related topics