UE5 Outer & GarbageCollection

에크까망·2024년 9월 29일

들어가기 전에

  • 단순화 하기 위해서 문체를 단정적으로 사용하지만 지극히 개인 의견일 뿐 입니다.
  • 본문과 관련해서 오류 지적이나 의견 있으시면 꼭 댓글 부탁드립니다.
  • 글 작성 시점은 2024/09 입니다.
  • 글 작성 기준은 UE5.4.4 입니다.

들어가며

UObject 에서 Outer 는 구조 를 유지 하는 기능 을 한다. 이번 포스팅에서는 대표적인 Outer 의 사용처를 먼저 살펴보고 GarbageCollection 에서 Outer 가 어떻게 작용되는지 살펴보자.

대표적인 사용처

인터넷의 다른 Article 에도 잘 나와 있지만 일단 World 구조를 유지 한다.

class UWorld* UObject::GetWorld() const  
{  
    if (UObject* Outer = GetOuter())  
    {       
	    return Outer->GetWorld();  
    }  
    
   return nullptr;  
}
UWorld* AActor::GetWorld() const  
{  
    // CDO objects do not belong to a world  
    // If the actors outer is destroyed or unreachable we are shutting down and the world should be nullptr    
    if (!HasAnyFlags(RF_ClassDefaultObject) && ensureMsgf(GetOuter(), TEXT("Actor: %s has a null OuterPrivate in AActor::GetWorld()"), *GetFullName())  
       && !GetOuter()->HasAnyFlags(RF_BeginDestroyed) && !GetOuter()->IsUnreachable())  
    {       
	    if (ULevel* Level = GetLevel())  
       {          
	       return Level->OwningWorld;  
       }    
    }    
    
    return nullptr;  
}

또 Packaging 등에서 Serializing 의 구조 또한 유지 한다.

Outer 가 유효하면 GC되지 않을까?

TestCode

UMyTestObj

UCLASS()  
class EKUE54_API UMyTestObj : public UObject  
{  
    GENERATED_BODY()  
public:  
    virtual void BeginDestroy() override;  
};
void UMyTestObj::BeginDestroy()  
{  
    UE_LOG(LogTemp, Warning, TEXT("UMyTestObj::BeginDestroy() - %s"), *GetName());  
    Super::BeginDestroy();  
}

AEkUe54GameMode

class AEkUe54GameMode : public AGameModeBase  
{  
    GENERATED_BODY()  
  
protected:  
    TWeakObjectPtr<class UMyTestObj> WeakTestObj;  
          
public:  
    AEkUe54GameMode();  
  
    virtual void BeginPlay() override;  
    virtual void Tick(float DeltaSeconds) override;  
    virtual void BeginDestroy() override;  
};
void AEkUe54GameMode::BeginPlay()  
{  
    Super::BeginPlay();  
  
    {       
	    UMyTestObj* MyTestObj = NewObject<UMyTestObj>(this);  
       WeakTestObj = MyTestObj;  
       GEngine->ForceGarbageCollection(true);  
    }  
}  
  
void AEkUe54GameMode::Tick(float DeltaSeconds)  
{  
    Super::Tick(DeltaSeconds);  
    UE_LOG(LogTemp, Warning, TEXT("WeakTestObj is - %s"), WeakTestObj.IsValid() ? TEXT("Valid") : TEXT("Invalid") );  
}  
  
void AEkUe54GameMode::BeginDestroy()  
{  
    UE_LOG(LogTemp, Warning, TEXT("AEkUe54GameMode::BeginDestroy() - %s"), *GetName());  
    Super::BeginDestroy();  
}

Scenario

UMyTestObj 를 생성하고 Outer 로 AEkUe54GAmeMode 를 지정 하였다. GameMode 는 해당 World 가 내려가기 전에는 파괴되지 않는다. 추적을 위해서 WeakTestObj 에 Referencing 하였다. 따라서 생성된 MyTestObj 로의 GC Referencing 은 없다.

Result

[2024.09.29-04.24.31:103][  0]LogTemp: Warning: UMyTestObj::BeginDestroy() [2024.09.29-04.24.31:794][  1]LogTemp: Warning: WeakTestObj is - Invalid

Outer 가 Valid 하지만 Referencing 이 없으니 파괴 된다.

Outer 가 그 상위로 부터 GC Referencing 되지 않으면 하위도 모두 파괴 될까?

TestCode

UMyTestObj

이전 시나리오와 동일 하다.

AEkUe54GameMode

class AEkUe54GameMode : public AGameModeBase  
{  
    GENERATED_BODY()  
  
protected:  
    UPROPERTY()  
    TObjectPtr<class UMyTestObj> KeepTestObj;  
    
    TWeakObjectPtr<class UMyTestObj> WeakTestObj;
    
    public:  
    AEkUe54GameMode();  
  
    virtual void BeginPlay() override;  
    virtual void Tick(float DeltaSeconds) override;  
    virtual void BeginDestroy() override;  
};
void AEkUe54GameMode::BeginPlay()  
{  
    Super::BeginPlay();  
  
    {       
	   UMyTestObj* MyTestObj = NewObject<UMyTestObj>(this);  
       WeakTestObj = MyTestObj;  
  
       UMyTestObj* MyTestSubObj = NewObject<UMyTestObj>(MyTestObj);  
       KeepTestObj = MyTestSubObj;  
  
       GEngine->ForceGarbageCollection(true);  
    }}  
  
void AEkUe54GameMode::Tick(float DeltaSeconds)  
{  
    Super::Tick(DeltaSeconds);  
    UE_LOG(LogTemp, Warning, TEXT("WeakTestObj is - %s"), WeakTestObj.IsValid() ? TEXT("Valid") : TEXT("Invalid") );  
}  
  
void AEkUe54GameMode::BeginDestroy()  
{  
    UE_LOG(LogTemp, Warning, TEXT("AEkUe54GameMode::BeginDestroy() - %s"), *GetName());  
    Super::BeginDestroy();  
}

Scenario

MyTestSubObj 를 만들고 Outer 로 이전에 GC되고 있는 상태였던 MyTestObj 를 지정해 주었다. 그리고 AEkUe54GameMode 에서 MyTestSubObj 로의 Referencing 을 유지 하고 있다.

Result

[2024.09.29-04.54.31:639][324]LogTemp: Warning: WeakTestObj is - Valid

MyTestObj 로의 Referencing 이 AEkUe54GameMode 로부터 걸려있지 않아도 잘 살아 있다. 이는 SubObject 인 MyTestSubObj 로 Referencing 이 걸려있고 MyTestSubObj 에서 Outer 인 MyTestObj 로 Referencing 이 걸리기 때문이다.

Outer GC 규칙

Outer 는 SubObject 의 GC 를 막지 않는다(Referencing 하지 않는다). 오히려 SubObject 로부터 Outer 로 Referencing 이 걸린다.

profile
Game Client Programmer

0개의 댓글