UE5 메모리 & GC
Unreal Engine의 메모리 관리는 두 축으로 나뉩니다. UObject 기반 가비지 컬렉션(GC) 과 C++ 스마트 포인터 입니다. 두 시스템의 경계를 이해하지 못하면 메모리 누수 또는 댕글링 포인터가 발생합니다.
UObject GC 원리
Section titled “UObject GC 원리”UObject를 상속한 객체는 UE의 GC가 수명을 관리합니다. GC는 마크 앤 스윕(Mark & Sweep) 방식으로 루트 셋에서 도달 불가능한 UObject를 수집합니다.
GC 루트 셋 ├─ AddToRoot()로 등록된 객체 ├─ UPROPERTY로 참조된 객체 └─ 현재 로드된 UWorld / Level기본적으로 매 60초마다 또는 힙 임계값 초과 시 실행됩니다.
; DefaultEngine.ini[/Script/Engine.GarbageCollectionSettings]gc.TimeBetweenPurgingPendingKillObjects=60.0gc.MaxObjectsNotConsideredByGC=1UPROPERTY — GC 참조 선언
Section titled “UPROPERTY — GC 참조 선언”UObject 포인터는 반드시 UPROPERTY로 선언해야 GC가 참조를 추적합니다.
UCLASS()class AMyActor : public AActor{ GENERATED_BODY()
// ✅ GC가 참조를 추적 — 이 객체가 살아있는 동안 Component도 수집 안 됨 UPROPERTY() UStaticMeshComponent* MeshComp;
// ❌ UPROPERTY 없음 — GC가 참조를 모르므로 수집될 수 있음 UStaticMeshComponent* UntrackedComp;};스마트 포인터 종류
Section titled “스마트 포인터 종류”| 포인터 | 대상 | 특징 |
|---|---|---|
TSharedPtr<T> | 비UObject C++ 객체 | 참조 카운팅, 스레드 안전 옵션 |
TWeakPtr<T> | 비UObject C++ 객체 | 소유권 없음, 유효성 확인 필요 |
TUniquePtr<T> | 비UObject C++ 객체 | 단독 소유, 복사 불가 |
TWeakObjectPtr<T> | UObject | UObject GC와 연동, 수집 시 자동 null |
TObjectPtr<T> | UObject (UE 5.0+) | UPROPERTY 대체, 에디터 추적 강화 |
TWeakObjectPtr 활용
Section titled “TWeakObjectPtr 활용”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, ...); }}메모리 누수 주요 원인
Section titled “메모리 누수 주요 원인”1. AddToRoot 미해제
Section titled “1. AddToRoot 미해제”// ❌ AddToRoot 후 RemoveFromRoot 누락 → GC 영구 제외UMyObject* Obj = NewObject<UMyObject>();Obj->AddToRoot();// RemoveFromRoot() 호출 안 함
// ✅ 사용 완료 후 반드시 해제Obj->RemoveFromRoot();2. 람다에서 UObject 캡처
Section titled “2. 람다에서 UObject 캡처”// ❌ 람다가 UObject를 강하게 캡처 → GC 수집 불가TFunction<void()> Callback = [this](){ // this(AActor*)가 수집되어도 람다가 참조를 유지 DoSomething();};
// ✅ TWeakObjectPtr로 캡처TWeakObjectPtr<ThisClass> WeakThis(this);TFunction<void()> Callback = [WeakThis](){ if (WeakThis.IsValid()) WeakThis->DoSomething();};3. 순환 참조
Section titled “3. 순환 참조”// ❌ A → B → A 순환 참조 → 둘 다 수집 안 됨UPROPERTY() AActorB* RefToB; // A가 B를 참조UPROPERTY() AActorA* RefToA; // B가 A를 참조
// ✅ 한 쪽을 TWeakObjectPtr로 변경UPROPERTY() TWeakObjectPtr<AActorA> WeakRefToA;IsValid vs IsValidLowLevel
Section titled “IsValid vs IsValidLowLevel”// IsValid() — GC 대기(PendingKill) 포함 null 체크 (일반적으로 이것 사용)if (IsValid(MyActor)) { }
// IsValidLowLevel() — 포인터 자체의 메모리 유효성만 체크 (엔진 내부용)// 일반 게임플레이 코드에서는 사용 금지메모리 프로파일링
Section titled “메모리 프로파일링”Unreal Insights → Memory Insights 탭 └─ 할당 추적, 태그별 메모리 사용량 확인
콘솔 명령어: memreport -full 전체 메모리 리포트 파일 생성 obj list class=Texture 클래스별 UObject 목록 출력 stat memory 실시간 메모리 통계