들어가며
C++ 언어 내부에는 Java, C#과는 다르게 GC(Garbage Collector)가 존재하지 않는다. 그니까 추적 메모리관리 시스템이 구현되어있지 않다.
Java, C#에는 Generation 방식으로 GC가 구현되어 있는데, 이건 지나가는 말로 ..
그러나 Unreal에서는 자체적으로 GC를 구현해 추적 메모리관리 시스템을 구현해놓았다.
작동 원리가 어떻게 되는가, 그리고 C++내부에서 메모리관리 시스템으로 구현해놓은 스마트포인터와의 차이점은 무엇일까 한편 살펴보는 차원에서 블로그 글을 작성한다.
Unreal GC는 어떤 방식으로 운용되는가 ?
Unreal GC는 Mark & Sweep 알고리즘을 이용해 RootSet에서부터 객체들을 참조하며 나아가 도달되지 못한 객체에는 Unreachable flag를 set하고, Sweep 단계에서 flag를 참고해 모두 제거한다.

그림과 같이 RootSet에서 참조그래프를 탐색하면서 도달 가능한지 불가능한지 파악 후 flag를 Mark 한다.
그럼 객체들을 GC가 어떻게 파악하고, 어떻게 제거하는가 ?
Unreal GC는 지정된 객체들만 등록되어 가비지컬렉션이 이루어지고, 그 지정된 객체들은 모두 UObject의 하위클래스이다.
UObject의 모든 하위클래스는 SpawnActor, CreateDefaultSubObject등 객체를 생성할 때 생성 과정에서 모두 GC의 추척대상으로 등록된다.
그러면 UObject를 상속받는 객체들은 GC에 등록되었다고 할 수 있다.
(그러나 참조그래프에 등록된것은 아님을 인지하자)
어디에서부터 GC가 시작되는가부터 알아보면 될 것 같은데, 막연하게 Ctrl+Q로 GarbageCollection을 검색해보면, GarbageCollection.cpp 파일이 존재.
Unreal GC는 언제 작동하는가 ?
Unreal 에서 GC는 설정된 주기에 따라 Engine Tick과 함께 작동한다.

Engine Tick에서 시작해 정해진 주기에 따라 CollectGarbage함수가 호출된다. 호출된 CollectGarbage함수에서는
CollectGarbage

CollectGarbageInternal 함수를 호출함. CollectGarbageInternal 함수는 우선
CollectGarbageInternal

Object들을 Reachability한지 분석하는 함수를 먼저 호출해준다. 이 안에서
PerformReachabilityAnalysis

Object들이 Unreachable한지 Reachable한지 Mark 해주는 과정을 거친다. MarkObjectsAsUnreachable함수를 잠깐 살펴보면, 다양한 조건문으로 Object를 검사 후 Flag를 부여한다.

Marking 후
이렇게 Marking을 진행한 후 Sweep 단계가 진행된다. CollectGarbageInternal 함수는 PerformReachabilityAnalysis함수 진행한 다음 코드에 위치해 있다.
CollectGarbageInternal

Incremental(점진적으로)하게 Garbage를 Purge(제거)하는 함수를 호출해준다.
IncreamentalPurgeGarbage

정확하게 코드분석을 해내진 못하겠지만, Object가 Unreachable한지 파악하고, Flag들도 파악하면서 Destroy하는 것 같아보임.
이 함수에 적힌 주석을 그대로 가져와보면,
Incrementally purge garbage by deleting all unreferenced objects after routing Destroy.
*
* Calling code needs to be EXTREMELY careful when and how to call this function as
* RF_Unreachable cannot change on any objects unless any pending purge has completed!
간단하게 말하면 Unreferenced된 Object들은 Incrementally하게 Purge된다는 이야기.
GC 마무리
Garbage Collection의 간단한 작동방식과 작동순서에 대해서 살펴봤다. 엄청 깊게 파고들어가서 어떻게 삭제가 진행되는지, 어떤 알고리즘으로 flag를 부여하는지 까지는 분석하지 못했지만, 실력이 늘어갈수록 이해할 수 있는 코드량이 많아질 것으로 생각된다.
스마트포인터 ?
Unreal엔진에서 제공하는 스마트포인터 종류 :
TUniquePtr -> 단 하나의 메모리만 다룸 여러 포인터들이 같은 메모리를 참조 못함.
TSharedPtr -> ref Count 증가 -> refCnt가 0이면 메모리 해제
TWeakPtr -> TSharedPtr을 참조해도 RefCnt가 증가하지 않음.
그럼 GC 와의 차이점은 ?
GC는 소유자를 몰라도 메모리를 사용중인지 확인하는 것.
자동적으로 사용중이지 않은 메모 리는 알고리즘에 따라서 제거됨.
스마트포인터는 소유자가 있다면 계속해서 유지되고, 소유자가 없어진다면 메모리를 해제하는 방식임.
제거 시기도 차이점이 있다고 생각하는데, 주기에 맞춰서 한꺼번에 Sweep하는 GC와 소유자의 변경에 따라 즉각적으로 제거되는 스마트포인터.
사실 GC는 무조건적으로 Mark and Sweep이 일어날 때 All Stop이 이뤄질 수 밖에 없는데, 스마트포인터는 그런게 없다고 보면 된다.
왜 스마트포인터가 아닌 GC를 ?
스마트포인터는 GC와의 성능(속도) 면에서 약 3배정도 차이난다고 하는데, 그것을 감안하고도 사용하는 것은 그만큼 자동화가 프로그래머 입장에서 편하고, 이 편함에 속도저하는 감안해도 될 부분이라고 생각되어서 그런 것 같다.