ShovelingLife
A Game Programmer
ShovelingLife
전체 방문자
오늘
어제
  • 분류 전체보기 (1067)
    • 그래픽스 (57)
      • 공통 (19)
      • 수학 물리 (22)
      • OpenGL & Vulkan (1)
      • DirectX (14)
    • 게임엔진 (180)
      • Unreal (69)
      • Unity (100)
      • Cocos2D-X (3)
      • 개인 플젝 (8)
    • 코딩테스트 (221)
      • 공통 (7)
      • 프로그래머스 (22)
      • 백준 (162)
      • LeetCode (19)
      • HackerRank (2)
      • 코딩테스트 알고리즘 (8)
    • CS (235)
      • 공통 (21)
      • 네트워크 (44)
      • OS & 하드웨어 (55)
      • 자료구조 & 알고리즘 (98)
      • 디자인패턴 (6)
      • UML (4)
      • 데이터베이스 (7)
    • 프로그래밍 언어 (346)
      • C++ (167)
      • C# (88)
      • Java (9)
      • Python (33)
      • SQL (30)
      • JavaScript (8)
      • React (7)
    • 그 외 (9)
      • Math (5)
      • 일상 (5)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • Source Code 좌측 상단에 복사 버튼 추가 완료
  • 언리얼 엔진 C++ 빌드시간 단축 꿀팁
  • 게임 업계 코딩테스트 관련
  • 1인칭 시점으로 써내려가는 글들

인기 글

태그

  • 백준
  • 함수
  • 유니티
  • C
  • 문자열
  • 알고리즘
  • c#
  • Unity
  • 클래스
  • 프로그래머스
  • 오블완
  • 포인터
  • 그래픽스
  • C++
  • 배열
  • 파이썬
  • 티스토리챌린지
  • string
  • SQL
  • 언리얼

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
ShovelingLife

A Game Programmer

게임엔진/Unreal

[Unreal] 가비지 컬렉터 (GC) 정리

2023. 10. 26. 11:45

언리얼 엔진은 Reference Graph 를 만들어 오브젝트들의 사용 여부를 구분한다. 이 그래프 루트에는 "Root Set" 이라고 지정된 오브젝트 셋이 있으며, "Root Set" 에 포함된 객체들은 GC 대상에서 제외된다(Mark & Sweep 방식으로 추적).

 

세 가지 규칙 :

  • UPROPERTY 선언 : 클래스 내부 멤버 변수가 클래스의 객체의 수명과 운명을 함께할 경우 선언
  • 멤버가 가리키는 포인터 : 엔진이 인식하거나 관리하지 않는 메모리 영역을 가리키도록 만들면 안됨
  • TArray 를 활용 : UObject 또는 자식들에 대한 포인터를 안전하게 담을 수 있는 유일한 컨테이너

기타 인터페이스 예시 :

// Object 를 살아있게 만드는 3가지 방법;
// 1. UPROPERTY 붙여주기 (참조된 객체로부터 강한 참조)
// 2. GC 가 참조하는 객체에서 UObject::AddReferencedObjects() 호출
// 3. 객체를 "Root Set" 에 추가 (일반적으로 불필요)
UObject::BaseUtility::AddToRoot();
UMyObject->AddToRoot();
 
 
// GC 수행 대상으로 등록 : MarkPendingKill, MarkAsGarbage 사용
UObject::ConditionalBeginDestroy(); // GC 명시적 요청, UObject
UObject::DestroyActor(); // GC 명시적 요청, AActor
 
// 강제로 가비지 컬렉션 수행
World::ForceGarbageCollection(bool bFullPurge);

 

가비지 컬렉터 퍼포먼스 튜닝 설정

  1. Create Garbage Collector UObject Clusters : 가비지 컬렉터 UObject 클러스터 생성(기본으로 켜져 있음). 관련된 오브젝트들을 하나의 가비지 컬렉션 클러스터에 묶어, 오브젝트 각각이 아닌 클러스터 하나만 검사할 수 있도록 해 놓음.
  2. Merge GC Clusters : GC 클러스터 병합. 한 클러스터의 오브젝트가 다른 클러스터의 오브젝트를 참조할 때 클러스터를 합치도록 함. Create Garbage Collector UObject Clusters 옵션도 켜져 있어야 함.
  3. Actor Clustering Enabled : 액터 클러스터 활성화. 프로젝트 세팅에서 옵션을 켜고, bCanBeInCluster 변수를 true 로 설정하거나, 코드에서 CanBeInCluster 함수가 true 를 반환하도록 덮어 쓰면 액터를 클러스터에 넣을 수 있다.
  4. Blueprint Clustering Enabled : 블루프린트 클러스터 활성화. 블루프린트의 UBlueprintGeneratedClass 및 관련 데이터(e.g. 공유 UPROPERTY 및 UFUNCTION 데이터)를 클러스터로 묶을 수 있다. 클러스터는 Blueprint Generated Class 자체를 참조하게 된다.
  5. Time Between Purging Pending Kill Objects : 킬 대기중 오브젝트 제거 간격. 프로젝트 세팅에서 가비지 컬렉션 발동 빈도를 조절할 수 있다.

 

오브젝트의 소멸 도중 호출되는 함수 정리

  1. BeginDestroy 소멸 시작 : 오브젝트의 메모리 해제, 기타(그래픽 스레드 프록시 오브젝트 등) 멀티스레드 리소스 처리. 소멸 예정 관련 대부분의 게임플레이 함수성은 이미 EndPlay 에서 처리됨.
  2. IsReadyForFinishDestroy 소멸 마무리 준비 여부 : 이 함수를 호출하여 오브젝트 할당을 영구히 해제할 준비가 되었는지 여부를 결정. false 를 반환하면, 이 함수는 다음 가비지 컬렉션 패스까지 실제 오브젝트 소멸 작업을 유예시킴.
  3. FinishDestory 소멸 마무리 : 오브젝트가 곧 소멸되므로, 내부 데이터 구조체를 해제시킬 마지막 기회임. 메모리 해제 이전 마지막 호출.
  • 생 포인터 타입이면서 UPROPERTY 로 선언된 경우, nullptr 가 된다. TArray, TSet, TMap 같은 컨테이너 내에서 생성된 생 포인터의 경우도 nullptr 가 되고, 삭제되지는 않는다.
  • GC 는 실제로 객체의 파괴가 수행될 때 UPROPERTY 포인터를 nullptr 로 바꾸므로, 해당 포인터들을 더 안전하게 사용하기 위해서는 해당 포인터가 가리키는 객체가 회수되기를 기다리고 있는지를 확인해야 한다. 이때 IsPendingKill 혹은 IsValid 를 사용한다.
// 포인터가 non-null 이고 not pending kill 인지 확인
bool IsValid(const UObject* Test);
 
// 아래 함수는 사용하지 말 것
UObjectBase::IsValidLowLevel        // UPROPERTY 포인터는 항상 true, Raw Pointer 는 삭제 여부에 따라 true/false 반환
UObjectBase::IsValidLowLevelFast    // 가리키는 메모리에 다른 값이 들어갔을 경우 Crash 를 일으킬 수 있음

위에서 주석으로 달아 놓은 '아래 함수는 사용하지 말 것' 이라는 뜻은, UPROPERTY 포인터에 한해서다.

UObject, AActor 타입 녀석들의 경우 IsValidLowLevel 을 사용해서 객체가 수거되었는지를 잘 확인할 수 있다. 언리얼 객체들은 GUObjectArray 라는 배열에 저장되는데, 해당 객체가 이 GUObjectArray 라는 배열에 잘 들어가 있는지를 확인하는 기능을 수행한다.

 

  • GC 가능 오브젝트를 참조하는 non-garbage 클래스를 만드려면 FGCObject 를 상속받은 후, AddReferencedObjects 를 직접 구현하면 된다.
  • raw pointer 를 사용하면 가리키는 대상이 제거되었는지 알 수 없다. 이 경우, TWeakObjectPtr 를 사용해야 한다. 자신이 참조하는 객체가 다른 곳에서 파괴된다면,  TWeakObjectPtr::IsValid() 는 false 를 리턴한다. 또한 루트 집합에 참조를 추가하지 않아 효율적이다(UObject 는 TWeakObjectPtr, Native C++ 클래스는 TWeakPtr).

 

언리얼 가비지 컬렉터(GC) 심화 정리 (koreanfoodie.me)

저작자표시 (새창열림)

'게임엔진 > Unreal' 카테고리의 다른 글

[Unreal] 메모리 관리  (0) 2023.10.26
[Unreal] Actor 와 ActorComponent 의 개념 (vs. Unity 에서의 GameObject 와 비교)  (0) 2023.10.26
[Unreal] 클래스 기본 객체 (CDO) + 로딩과정 + 로그디버깅 + 인스턴스 생성  (1) 2023.08.31
[Unreal] 언리얼 빌드 시스템 + Target.cs  (0) 2023.08.28
[Unreal] 스레드와 단일 스레드로 실행시키기 (-norenderthread)  (0) 2023.08.12
    '게임엔진/Unreal' 카테고리의 다른 글
    • [Unreal] 메모리 관리
    • [Unreal] Actor 와 ActorComponent 의 개념 (vs. Unity 에서의 GameObject 와 비교)
    • [Unreal] 클래스 기본 객체 (CDO) + 로딩과정 + 로그디버깅 + 인스턴스 생성
    • [Unreal] 언리얼 빌드 시스템 + Target.cs
    ShovelingLife
    ShovelingLife
    Main skill stack => Unity C# / Unreal C++ Studying Front / BackEnd, Java Python

    티스토리툴바