Ch03 Memory

11 분 소요

GC

언리얼 위키, 꼭 읽기

원리

Reference Graph 를 주기적으로 탐색해 메모리를 지움.

Reference 에 잡는 법은 아래의 3가지 방법으로 1번째 방법으로 왠만한 경우가 해결 됨.

  • UPROPERTY 로 잡은__,__ 포인터, static array(int a[10] 같은거) 또는 UE4 가 제공하는 컨테이너(TARRAY 같은거 )
  • UObject::AddReferencedObjects 의 오버라이드로 추가
    • Actor-Component 가 이걸로 기본으로 등록됨
  • UObject::AddToRoot 인 메소드 호출
    • Root 에 있는건 삭제되지 않는데 어쩔 수 없는 경우에만 사용

레퍼런스가 안잡히는 UObject 이하의 애들은 다음 GC Cycle 에 삭제 됨.

UStructUObject 의 자식이 아니기 때문에 GC 처리되지 않으므로 필요하면 위의 3가지마 방법으로 레퍼런스를 잡아줘야함.

GC Cycle 은 Game Thread 에서 실행되므로 Object 내부의 함수 도중에 GC 는 시행안됨 .

  • 중간에 GetWorld()->ForceGarbageCollection(true); 로 실행시는 보장안됨.

사용

NewObject 로 생성한 애들이 적용되지, SpawnActor 는 적용 안됨 .

UPROPERTY 애들은 삭제될 때 nullptr 로 초기화 됨.

bool IsValid(const UObject* test) 함수가 삭제 체크검사 + null 체크로 유용함

GetWorld()->ForceGarbageCollection(true); 로 어쩔 수 없을 때 강제 로 GC 가능.

  • 단 이때 함수 도중에 GC 가 발동되므로 IsValid 로도 삭제예정인지 할 수 없음. 얘는 삭제할 예정인지만 검사할 수 있기 때문임. 그러므로 GameMode 의 Load 등에만 사용권장.

직접 만든 객체를 UE4 가 관리하는 GC System 에 넣으려면 FGCObject 를 상속해야함.

GC 가 삭제한 메모리 참조시 Read Access Violation - 0XFFFFF 어쩌로 그럼.

기타

Project Setting -> Engine -> Garbage Collection 에 옵션이 있음.

생성

Create

  • Instance
    • 예제코드
      auto actor = NewObject<UObject>(this, strref_class.TryLoadClass<AActor>(), "Name");
      actor->ConditionalBeginDestroy();  // 수동메모리해제요청(다음 GC 에 해제)
      
    • 참고
    • outer 에 대해서
    • ConstructObject is deprecated . UClass 로 생성하는 경우 NewObject 의 두번째 인자에 넣어주면 오버로딩이 됨.(참고)
    • 소멸 및 GC 요청은 ConditionalBeginDestroy() 메소드 호출
  • World Spawn
    • 월드 배치는 AActor 부터 월드에 대한 Transform 을 객체가 갖기 때문에 접두사가 A 가 붙은 클래스만 가능함.
    • GetWorld()->SpawnActor<AActor>(strref_class.TryLoadClass<AActor>()) 이런식
    • 에디터 상에서 표시되는 Subclass 의 경우 CreateDefaultSubobject생성자 에서 씀.
    • 소멸 및 GC 요청은 Destroy() 메소드 호출.

Smart Ptr

사용자 정의 자료형용

  • 설명
    • 참고
    • 플랫폼 간의 호환성으로 사용하지 않는 STL 의 smart pointer 와 거의 비슷한 내용임
    • UE4 차원에서 생성 관리하므로 UObject 에서 쓰면 안됨.(주로 소멸시 터짐)
    • 아직 배열단위로 관리가 안됨(소멸자가 안됨)
  • TUniquePtr
    • 스레드에서 안전하지 않음.
    • 복사 생성자 만날 시 소유권이 이전되어 전달하던 포인터는 유효하지 않게 됨.
  • TSharedPtr
    • Template 2번째 인자로 스레드 안전한한 포인터도 쓸 수 있음.
    • TSharedRef 으로 변환가능한데 차이는 Get() 사용시 reference 형태로 리턴된다는 것.
    • 형변환은 Upcasting 의 경우 묵시적 변환이 되고, Downcasting 의 경우StaticCastSharedPtr<BBB>(shared_aaa);StaticCastSharedRef<BBB>(shared_aaa); 로 가능.
  • TWeekPtr
    • shared_ptr 에서 묵시적으로 변환가능.
    • TSharedPtr 의 참조카운터를 올리지 않고 그 SharedPtr 가 유효한지 아닌지 파악가능
    • 상대적으로 느림

UObject 이하 자료형용

  • TWeakObjectPtr
    • GC Refence Graph 에 잡히지 않는 UObject 이하의 애들은 다음 GC 에 사라질 위협에 있음.
    • 그렇다고 모든 UObject 이하의 애들을 UPROPERTY 로 놔둘수는 없음
    • 그렇다고 그냥 포인터로는 GC 에서 사라졌는지 체크가 안됨.
    • TWeakObjectPtr 은 가능함.
    • MakeWeakObjectPtr<T>(T*) 로 생성가능
  • TSoftObjectPtr
    • 참고할 UE4 공식 문서
    • 원하는 객체에 대한 String 을 가지는 FSoftObjectPath 을 래핑하는 포인터
    • 비동기 로딩을 하는 객체를 지정하는 포인터가 될 수 있음
      • return Loaded ? obj else nullptr;
      • 단순히 널체크로도 확인할 수 있고 IsPending() 으로 지금 로드중인지 확인가능
      • 로드 중인지 체크만 해주는거고 로드는 코드 등으로 직접 해줘야햠.
    • 특히 유용한 경우는 UPROPERTY 로 만든 TSoftObjectPtr 을 Level 의 정적 객체와 연결할 수 있다는 것임.
TWeakObjectPtr<AActor> weak_actor;
AActor* normal_ptr;
normal_ptr = NewObject<AActor>(this, strref_class.TryLoadClass<AActor>(), "Name");
weak_actor = MakeWeakObjectPtr(normal_ptr);
PFCPP::Log(weak_actor.IsValid() ? "Weak is valid" : "Weak is invalid");
PFCPP::Log(normal_ptr != nullptr ? "Normal is valid!!" : "Normal is invalid!!");
		
GetWorld()->ForceGarbageCollection(true);  // normal_ptr is directing garbage after next GC
FTimerDelegate on_sec = FTimerDelegate::CreateLambda([weak_actor, normal_ptr]() {
	PFCPP::Log(weak_actor.IsValid() ? "Weak is valid!!" : "Weak is invalid!!");  /
	PFCPP::Log(normal_ptr != nullptr ? "Normal is valid!!" : "Normal is invalid!!");
	// normal_ptr->GetWorld(); // Crash
});

static FTimerHandle handle;
GetWorld()->GetTimerManager().SetTimer(handle, on_sec, 0.1f, false);

댓글남기기