언리얼 엔진/C++

언리얼엔진 C++ 가비지컬렉션

mane 2022. 1. 3. 14:44
728x90
반응형

프로그래밍을 하다 보면 동적으로 할당해준 메모리에 대한 관리를 계속해서 해줘야 한다.

우리의 컴퓨터는 일정한 메모리를 갖고 있고, 정해진 메모리를 초과하게 되면 동작하는 프로그램이 정상적으로 작동하지 않을 수가 있기 때문이다.

 

메모리 관리에 도움 되는 기법 중 하나로 가비지 컬렉터(grabage Collection)가 있고, 해당 기법은 프로그램이 동적으로 할당했던 메모리 영역 중에 필요 없게 된 영역을 해제하는 기능이다. 

 

가비지 컬렉션의 장점과 단점

다음과 같은 버그를 줄이거나 막을 수 있다.

 

장점

1. 유효하지 않은 포인터 접근을 방지한다.
2. 이중 해제로 이미 해제된 메모리를 또다시 해제하려고 하면 문제가 생길 수도 있다.
3. 메모리 누수로 더 이상 필요하지 않은 메모가 해제되지 않고 남아 있게 되면, 사용할 수 있는 메모리가 적어져서 프로그램이 문제가 생길 수도 있다.

 

단점

1. 어떤 메모리를 해제할지 결정하는데 비용이 든다는 것이다. 객체가 필요 없어지는 시점을 프로그래머가 미리 알고 있는 경우에도 가비지 컬렉터가 추적을 하긴 해야 하므로, 오버헤드가 된다.
2. 작동되는 타이밍이나 점유 시간을 예측하기가 어렵다. 이런 동작에 프로그램에도 약간의 영향을 갈 수도 있다.
3. 할당된 메모리가 해제되는 시점을 알 수가 없다. 자원 할당과 변수 초기화를 일치하는 RAII 스타일의 프로그래밍에서는 이것은 자원 해제 시점을 알수 없다는 것을 의미한다.

 

 

언리얼 엔진에서의 가비지 컬렉션

이런 메모리 관리기법으로 가비지 컬렉션은 자바, C# 등 일부 언어에서 염두에 두고 설계가 되어 있지만, C++ 기반인 언리얼 엔진에서도 자체적으로 지원하고 있다.

 

언리얼에서는 더 이상 참조되지 않고나, 명시적으로 소멸 예약시킨 UObject를 주기적으로 정기하는 가비지 컬렉션 스키마를 사용한다. 엔진에서는 레퍼런스 그래프를 만들어 오브젝트가 사용 중인지, 소멸 대기 중인지는 알아낸다. 이 그래프 루트에는 루트 세트(Root set)라 지정된 오브젝트 세트가 있다. 어떤 오브젝트도 루트 세트에 추가시킬 수 있다.

가비지 컬렉션이 발생하면, 엔진은 루트부터 시작해서 알려준 UObject 레퍼런스 트리를 검색하여 참조된 오브젝트를 전부 추적하고,

참조되지 않은 오브젝트 즉, 트리 검색에서 찾지 못한 것들을 필요하지 않다고 편하고 제거한다.

루트세트에서 참조되지 않아 가비지컬렉터에 수집된 2개의 UObject

이러한 방식은 Mark & Sweep(마크 앤 스윕) 방식이라고 불리며, 루트 세트로부터 Mark 되고, Mark 되지 않은 오브젝트들을 Sweep 된다고 말할 수 있겠다.

 

 

 

언리얼엔진에서의 가비지컬렉션 관련설정

언리얼 오브젝트가 GC시스템에 의해 자동 관리되기 위해서는 선언에 UPROPERTY 매크로를 해줘야 한다.

GC시스템이 언리얼 오브젝트를 관리하는 방식은 스마트 포인터에서 공유 포인터와 유사하다.

 다만 다른 점은 중앙의 GC시스템에 의해 모든 관리가 되기 때문에, 메모리에서 해지되는 타이밍을 정확히 예측할 수 없다. 따라서 언리얼 오브젝트의 포인터를 소멸할 때에는 BeginConditionalDestroy()라는 함수를 호출해주고, 시스템이 해지해줄 때까지 기다리는 수밖에 없다. GC는 엔진의 프로젝트 세팅에서 지정된 시간마다 언리얼 오브젝트를 감지해서 제거해준다.

기본 설정값은 약 61초이다.

GetWorld()->ForceGarbageCollection(true); 를 사용하면 GC 시스템에게 바로 자원회수를 명령할 수도 있다.

 

 

언리얼 오브젝트 메모리 관리는 공유 포인터와 동일한 방식으로 동작하기 때문에, 위에서 언급한 공유 포인터의 순환 참조의 문제에서 자유롭지 않습니다. 그래서 언리얼 C++은 언리얼 오브젝트를 약하게 참조하는 TWeakObjectPtr이라는 별도의 라이브러리를 제공하고 있습니다.  특정 언리얼 오브젝트를 참조할 때 반드시 소유권이 필요하지 않은 경우에는 약 참조를 걸어주는 것을 추천합니다. 

예를 들어 UI의 리스트 박스에서 언리얼 오브젝트의 목록을 보여주고 싶을 때에는 TWeakObjectPtr을 사용해 언리얼 오브젝트를 약 참조(Weak Referencing)하는 것이 바람직합니다.  일반 참조를 걸게 되면 UI가 띄워져 있는 동안에는 UI에서 보이는 모든 언리얼 오브젝트의 레퍼런스 카운팅이 올라가게 되므로 언리얼 오브젝트를 삭제해도 GC시스템에서 회수가 일어나지 않습니다. 이는 많은 분들이 간과하는 사항입니다. 이번 강좌를 통해 약 참조의 용도를 잘 기억해두시면 언젠가는 도움되지 않을까 생각합니다. 

출처 : https://m.blog.naver.com/destiny9720/220951194710

 

 

참고 : 

https://docs.unrealengine.com/4.27/ko/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/Objects/Optimizations/

 

언리얼 오브젝트 처리

UObject 시스템의 기능에 대한 개요입니다.

docs.unrealengine.com

https://docs.unrealengine.com/4.27/ko/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/SmartPointerLibrary/

 

언리얼 스마트 포인터 라이브러리

위크 포인터 및 Null이 불가능한(non-nullable) 쉐어드 레퍼런스와 같은 쉐어드 포인터들의 커스텀 구현입니다.

docs.unrealengine.com

 

출처 : https://hyo-ue4study.tistory.com/272?category=878133

728x90
반응형