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 слоем.