Skip to content

UE5 메모리 & GC

Unreal Engine의 메모리 관리는 두 축으로 나뉩니다. UObject 기반 가비지 컬렉션(GC)C++ 스마트 포인터 입니다. 두 시스템의 경계를 이해하지 못하면 메모리 누수 또는 댕글링 포인터가 발생합니다.


UObject를 상속한 객체는 UE의 GC가 수명을 관리합니다. GC는 마크 앤 스윕(Mark & Sweep) 방식으로 루트 셋에서 도달 불가능한 UObject를 수집합니다.

GC 루트 셋
├─ AddToRoot()로 등록된 객체
├─ UPROPERTY로 참조된 객체
└─ 현재 로드된 UWorld / Level

기본적으로 매 60초마다 또는 힙 임계값 초과 시 실행됩니다.

; DefaultEngine.ini
[/Script/Engine.GarbageCollectionSettings]
gc.TimeBetweenPurgingPendingKillObjects=60.0
gc.MaxObjectsNotConsideredByGC=1

UObject 포인터는 반드시 UPROPERTY로 선언해야 GC가 참조를 추적합니다.

UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
// ✅ GC가 참조를 추적 — 이 객체가 살아있는 동안 Component도 수집 안 됨
UPROPERTY()
UStaticMeshComponent* MeshComp;
// ❌ UPROPERTY 없음 — GC가 참조를 모르므로 수집될 수 있음
UStaticMeshComponent* UntrackedComp;
};

포인터대상특징
TSharedPtr<T>비UObject C++ 객체참조 카운팅, 스레드 안전 옵션
TWeakPtr<T>비UObject C++ 객체소유권 없음, 유효성 확인 필요
TUniquePtr<T>비UObject C++ 객체단독 소유, 복사 불가
TWeakObjectPtr<T>UObjectUObject GC와 연동, 수집 시 자동 null
TObjectPtr<T>UObject (UE 5.0+)UPROPERTY 대체, 에디터 추적 강화

UObject를 소유하지 않고 참조만 유지할 때 사용합니다. GC가 대상을 수집하면 자동으로 무효화됩니다.

// 약한 참조 저장
TWeakObjectPtr<AEnemy> WeakEnemy;
void AMyController::TrackEnemy(AEnemy* Enemy)
{
WeakEnemy = Enemy; // 소유권 없이 참조만 저장
}
void AMyController::Tick(float DeltaTime)
{
// IsValid()로 유효성 확인 후 사용
if (WeakEnemy.IsValid())
{
WeakEnemy->TakeDamage(10.f, ...);
}
}

// ❌ AddToRoot 후 RemoveFromRoot 누락 → GC 영구 제외
UMyObject* Obj = NewObject<UMyObject>();
Obj->AddToRoot();
// RemoveFromRoot() 호출 안 함
// ✅ 사용 완료 후 반드시 해제
Obj->RemoveFromRoot();
// ❌ 람다가 UObject를 강하게 캡처 → GC 수집 불가
TFunction<void()> Callback = [this]()
{
// this(AActor*)가 수집되어도 람다가 참조를 유지
DoSomething();
};
// ✅ TWeakObjectPtr로 캡처
TWeakObjectPtr<ThisClass> WeakThis(this);
TFunction<void()> Callback = [WeakThis]()
{
if (WeakThis.IsValid())
WeakThis->DoSomething();
};
// ❌ A → B → A 순환 참조 → 둘 다 수집 안 됨
UPROPERTY() AActorB* RefToB; // A가 B를 참조
UPROPERTY() AActorA* RefToA; // B가 A를 참조
// ✅ 한 쪽을 TWeakObjectPtr로 변경
UPROPERTY() TWeakObjectPtr<AActorA> WeakRefToA;

// IsValid() — GC 대기(PendingKill) 포함 null 체크 (일반적으로 이것 사용)
if (IsValid(MyActor)) { }
// IsValidLowLevel() — 포인터 자체의 메모리 유효성만 체크 (엔진 내부용)
// 일반 게임플레이 코드에서는 사용 금지

Unreal Insights → Memory Insights 탭
└─ 할당 추적, 태그별 메모리 사용량 확인
콘솔 명령어:
memreport -full 전체 메모리 리포트 파일 생성
obj list class=Texture 클래스별 UObject 목록 출력
stat memory 실시간 메모리 통계