UObject Lifecycle
UObject Lifecycle과 GC 최적화
Unreal Engine의 UObject
는 엔진이 제공하는 기본적인 객체 시스템이다.
일반 C++ 객체와는 달리, Garbage Collector(GC)에 의해 메모리 해제가 자동으로 관리된다.
하지만 GC는 비용이 큰 작업이며, 구조를 잘 이해하지 못하면 메모리 누수나 불필요한 객체 유지로 성능 저하가 발생할 수 있다.
기본 개념: UObject의 수명 주기
NewObject<T>()
또는DuplicateObject()
등으로 객체 생성- 참조가 살아있는 동안 GC에서 유지됨
- 더 이상 유효한 참조가 없고, 다음 GC 주기에 포함되면 삭제 대상이 됨
- GC는
MarkPendingKill()
플래그를 통해 삭제 예정 객체를 표시 - 이후 실제
Destroy()
호출 또는 메모리 회수로 제거됨
핵심 요소 정리
요소 | 설명 |
---|---|
IsValid(Object) | nullptr 이 아니고 MarkPendingKill() 상태도 아닌지 확인하는 함수 |
MarkPendingKill() | 삭제 예정 표시. GC 대상이 됨 |
RF_Transient | 저장되지 않고, Editor/런타임 재시작 시 사라지도록 만드는 플래그 |
AddToRoot() | GC 대상에서 제외됨. 반드시 RemoveFromRoot() 와 함께 사용해야 함 |
TWeakObjectPtr | GC된 객체는 자동으로 nullptr가 됨. 안전한 참조 방식 |
TStrongObjectPtr | GC 보호가 되는 스마트 포인터. AddToRoot 없이도 안전함 |
MarkPendingKill()과 IsValid()
GC가 객체를 제거할 때는 내부적으로 MarkPendingKill()
을 호출하여 객체가 삭제 대상임을 표시한다.
이 상태인 객체에 접근하면 예기치 않은 동작이나 크래시가 발생할 수 있으므로, 항상 IsValid()
로 확인해야 한다.
if (IsValid(MyObject))
{
MyObject->DoSomething();
}
IsValid()
는 다음 조건을 모두 확인한다:
MyObject != nullptr
!MyObject->IsPendingKill()
RF_Transient 플래그
RF_Transient
는 객체가 저장되지 않도록 만드는 중요한 플래그이다.
MyObject->SetFlags(RF_Transient);
이 플래그가 설정된 객체는 다음과 같은 특징이 있다:
- 저장되지 않음 (SaveGame, Editor Save 등 무시)
- 객체가 일시적인 경우 적절한 선택
- 보통 UI 위젯, 비주얼 이펙트, 미리보기 객체 등에 사용
AddToRoot() / RemoveFromRoot()
Garbage Collector는 UObject를 Root Set에 포함하지 않으면 언제든지 삭제할 수 있다.
따라서 수동으로 GC 대상에서 제외하고 싶다면 AddToRoot()
를 호출해야 한다.
MyObject->AddToRoot(); // 절대 GC 안 됨
...
MyObject->RemoveFromRoot(); // 다시 GC 가능해짐
주의: AddToRoot()
를 호출하면 반드시 RemoveFromRoot()
를 해줘야 하며, 그렇지 않으면 메모리 누수가 발생할 수 있다.
스마트 포인터를 활용한 GC 안전 처리
TWeakObjectPtr
TWeakObjectPtr<UMyObject> WeakRef = MyObject;
...
if (WeakRef.IsValid())
{
WeakRef->DoSomething();
}
- GC가 객체를 제거하면 자동으로 nullptr이 된다.
- 유효성 검사 후 사용해야 한다.
TStrongObjectPtr
TStrongObjectPtr<UMyObject> StrongRef = TStrongObjectPtr<UMyObject>(MyObject);
- 객체가 GC에 의해 제거되지 않도록 보호함
- AddToRoot 없이도 안전함
- 라이프사이클을 명확하게 관리하고 싶을 때 유용하다
일반적인 최적화 전략
의미 없는 UObject Tick 제거
UObject는 기본적으로 Tick 대상이 아니지만, 타이머나 델리게이트 등에 연결되면 Tick처럼 동작할 수 있다.
필요 없으면 해제해야 한다.Transient 플래그 적극 활용
저장 필요 없는 일시적 객체는 모두RF_Transient
로 처리하는 것이 좋다.AddToRoot 남용 금지
GC를 완전히 막기 때문에 필요한 경우에만 사용해야 한다.스마트 포인터 사용
TWeakObjectPtr
,TStrongObjectPtr
를 통해 명시적이고 안전한 객체 관리가 가능하다.UObject를 new/delete로 만들지 말 것
반드시NewObject<T>()
,DuplicateObject()
또는CreateDefaultSubobject()
사용