언리얼 엔진의 가비지 컬렉터(GC)
언리얼 엔진의 가비지 컬렉터
언리얼 엔진은 UObject에 대한 객체 관리를
자동으로 해주며 프로그래머가 직접 메모리를 해제하지 않아도
엔진 영역에서 메모리를 관리해 준다
-
- GC(Garbage Collection)?
- 더 이상 사용되지 않는 객체(데이터)를 자동으로 찾아
메모리에서 제거하는 시스템을 말함
C,C++ 처럼 수동으로 메모리 관리를 해주는 언어가 아니면
대부분 GC가 메모리 관리를 대신해준다
- GC(Garbage Collection)?
작동 방식
언리얼 GC는 Mark and Sweep 알고리즘 기반
- Mark (표시) 단계
- GC가 Root 객체들에서 시작해, ‘연결’된 객체들을 추적
- UObject 중 UPROPERTY() 매크로로 마킹된 포인터를 추적
- ‘사용 중’인 객체를 모두 mark 한다
- GC가 Root 객체들에서 시작해, ‘연결’된 객체들을 추적
- Sweep (청소) 단계
- 표시되지 않은 객체들을 ‘사용하지 않는다고 간주’하고 파괴(destroy)
- 메모리도 같이 해제
- 표시되지 않은 객체들을 ‘사용하지 않는다고 간주’하고 파괴(destroy)
참고 매크로 & 함수
항목 | 설명 |
---|---|
UPROPERTY() |
GC가 추적할 수 있도록 포인터를 표시합니다. 이걸 안 하면 객체가 GC 대상이 됩니다. |
AddToRoot() |
객체를 GC 루트로 강제 등록해서 영구적으로 유지합니다. (보통은 권장되지 않음) |
IsPendingKill() |
객체가 GC에 의해 삭제 대기 중인지 확인할 때 사용해요. |
CollectGarbage() |
강제로 GC를 실행시키는 함수입니다 (주로 에디터나 개발 도중 테스트용). |
유의할 점으로
UPROPERTY()를 붙이지 않으면
GC가 ‘참조 중’이라는 사실을 모르기에
해제 해 버릴 수 있다는 점
UPROPERTY() // 이거 안 붙이면
UMyObject* MyObj; // 이걸 '참조'하는지 모르기에 메모리 해제 당할 수 있음
Root Set
언리얼 GC가 Mark 단계에서 ‘시작점’으로 삼는
UObject 집합들을 Root Set 이라 한다
(해당 집합에 있는 객체들은 ‘살아있는 객체’로 취급)
Root Set에 포함되는 조건들
포함 이유 | 설명 |
---|---|
RF_RootSet 플래그 설정됨 |
AddToRoot() 호출 시 자동으로 설정 |
엔진 내부 루트로 등록됨 | 예: 게임 인스턴스, 월드 등 |
에디터나 런타임에서 참조 중 | 예: 뷰포트에 표시되는 액터 등 |
UPROPERTY() 로 참조되고 있음 |
GC가 해당 객체를 “사용 중”으로 판단 가능 |
Unreal GC 동작 정리
결국 다음과 같은 방식으로 Unreal GC가 동작한다
- Root Set 시작
- 해당 집합 개체 식별
- ‘항상 살아 있는 객체’ 취급
(ex : Player Controller, Engine 관련 객체)
- 해당 집합 개체 식별
- Mark 단계
- Root Set 객체에서 시작
- 직간접 참조하는 UObject들에 대한 마킹
(객체가 사용중이라는 확인) - 즉, UPROPERTY()로 선언된 포인터만 따라간다
(Reflection 시스템에 기반)
- Root Set 객체에서 시작
- Sweep 단계
- 마킹 되지 않은 객체들의 메모리 회수
- 소멸자 호출 및 메모리 반환
- 파괴 순서
- RF_BeginDestroyed 설정 -> BeginDestroy() 호출
- 엔진에서 IsReadyForFinishDestroy() 체크
- 준비 완료 시 RF_FinishDestroyed 설정 → FinishDestroy() 호출
- 메모리 해제
- 마킹 되지 않은 객체들의 메모리 회수
GUObjectArray 와 플래그?
GUObjectArray 는 Unreal Engine 내부에서
모든 UObject 인스턴스를 추적 및 관리하는
전역 객체 배열 이다
역할 | 설명 |
---|---|
UObject의 전체 목록 저장 | 게임 내 모든 UObject의 생명주기를 추적 |
인덱스 기반 접근 지원 | 내부적으로 FObjectHandle /FObjectItem 등을 통해 빠른 검색 |
GC에서 핵심 역할 | Mark & Sweep 시 전부 순회하며 GC 대상 여부 판단 |
GUObjectArray 내부의 FObjectItem 배열에서
각 객체의 EObjectFlags 플래그들을 관리한다
(FObjectItem 내부에서 플래그 관리)
주요 EObjectFlags 플래그
-
- RF_RootSet
- 객체를 GC의 Root Set에 포함
AddToRoot() 호출 시 자동 설정
반대로 RemoveFromRoot 호출 시 플래그 제거
- RF_RootSet
-
- RF_BeginDestroyed
- 객체가 GC에 의하여 파괴 타켓이 됨
이 플래그가 설정된 순간 ‘유효하지 않은’ 상태가 됨
이 플래그 설정 후,BeginDestroy()가 호출된다
- RF_BeginDestroyed
-
- RF_FinishDestroyed
- 객체가 완전히 파괴되었음을 나타냄
메모리 해제 직전의 상태
FinishDestroy()가 호출된 후, 설정
- 함수 호출 직후, 바로 사라지는 것이 아니라
메모리 해제를 기다리는 상황이며
일정 시간 후, 해제되기에 설정하는 플래그
- RF_FinishDestroyed
그 외에도
RF_ 플래그들이 존재
관련 공식 문서
Unreal의 리플렉션
UObject 기반의 클래스들을
엔진에서 읽을 수 있도록 클래스 타입을 공유하는 시스템
언리얼 내부의 동작하는 다양한 모듈도 UObject 기반
다만, 사용자가 정의한 ‘타입’을 엔진에서 알지 못하므로
이를 처리할 수 있도록 ‘타입 정보’를 공유해야 함
=> 이를 위하여 리플렉션이 필요
- 리플렉션 시스템?
클래스, 함수, 변수 등에 대한 정보를 코드로부터 추출하고,
런타임이나 에디터에서 사용 가능하게 만드는 시스템
UHT (Unreal Header Tool)
Unreal Build Tool(UBT)의 일부로서
UCLASS/USTRUCT/UPROPERTY/UFUNCTION 등을
파싱해서 메타데이터 코드를 자동 생성하는 툴
- 헤더 파일에서 UCLASS(), UPROPERTY(), UFUNCTION() 등을 파싱
- .generated.h 파일을 생성
- 해당 파일이 포함하는 것들
- 클래스 등록 정보 (StaticClass(), StaticRegisterNatives())
- 리플렉션 데이터 (FProperty, FFieldClass, MetaData, FieldPath 등)
- 에디터/GC/블루프린트 호환용 메서드
- 클래스 등록 정보 (StaticClass(), StaticRegisterNatives())
리플렉션 관련 매크로
매크로 | 리플렉션에서의 목적 | 일반적인 위치 |
---|---|---|
UCLASS() |
C++ 클래스를 UObject 기반 리플렉션 시스템에 등록 |
클래스 정의 앞 |
UPROPERTY() |
멤버 변수를 리플렉션 시스템에 노출 | 멤버 변수 선언 앞 |
UFUNCTION() |
멤버 함수를 리플렉션 시스템에 노출 | 멤버 함수 선언 앞 |
USTRUCT() |
C++ 구조체를 리플렉션 시스템에 등록 | 구조체 정의 앞 |
GENERATED_BODY() |
UHT가 생성하는 리플렉션 및 엔진 지원 코드를 위한 삽입 지점 | 클래스/구조체 본문 첫 줄 |
정리
요약하자면,
Unreal 리플렉션 시스템은
사용자가 만든 클래스를 UObject 기반의 엔진 시스템과
호환되도록 제공하는 메타데이터 기반의 인프라이다
(GC, Editor, BP, Serialization 등이 모두 이러한
엔진 기능이므로 이 들과 사용자 정의 클래스를 연결해준다)
댓글남기기