언리얼 엔진의 메모리 관리

Lee Raccoon·2024년 6월 27일
0

언리얼 공부

목록 보기
8/11

C++은 프로그래머가 직접 new, delete로 할당과 해지를 해주어야했다.
이걸 잘못 관리하면 아래와 같은 문제점이 나타나게 된다.

  • Memory Leak : 메모리가 해지되지 않아서 힙에 메모리가 그대로 남음
  • Dangling Pointer : 이미 해제된 메모리의 주소를 가르키는 포인터
  • Wild Pointer : 값이 초기화 되지 않아서 엉뚱한 주소를 가르키는 포인터

이런 실수를 줄이기 위해 많은 프로그래밍 언어들이 가비지 컬렉터를 사용한다.
언리얼 엔진도 이런 가비지 컬렉션 시스템을 사용하고, 그 중 마크-스윕 방식을 사용한다.

가비지 컬렉션

프로그램에서 더 이상 사용하지 않는 오브젝트를 자동으로 감지해 메모리를 회수하는 시스템이다.

마크-스윕 방식

  1. 저장소에서 최초 검색을 시작하는 루트 오브젝트를 표기한다.
  2. 루트 오브젝트가 참조하는 객체를 찾아 마크한다.
  3. 마크된 객체로부터 다시 참조하는 객체를 찾아 마크하고 이를 계속 반복한다.
  4. 이제 저장소에는 마크된 객체와 마크되지 않은 객체의 두 그룹으로 나뉜다.
  5. 가비지 컬렉터가 저장소에서 마크되지 않은 객체들의 메모리를 회수(스윕)한다.

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

이런 가비지 컬렉션을 백그라운드에서 계속 돌리는 것이 생각보다 비용이 꽤 든다.
이것을 조정할 수 있도록 프로젝트 설정에서 다양한 값을 변경할 수 있다.

언리얼에는 관리되는 모든 언리얼 오브젝트의 정보를 저장하는 전역 변수 GUObjectArray가 존재한다.
이 변수의 각 요소에는 Flag가 설정되어 있다.
주요 Flag로는 아래와 같은 친구들이 있다.
가비지 컬렉터는 여기의 플래그를 주기적으로 확인해서 메모리를 회수해간다.

  • Garbage Flag : 다른 언리얼 오브젝트로부터의 참조가 없어 회수 예정
  • RootSet Flag : 다른 언리얼 오브젝트로부터의 참조가 없어도 회수하지 않는 오브젝트
    (GUObjectArray에서 제공하는 AddToRoot 함수를 호출하여 설정 가능, 근데 별로 쓰지는 않음)

언리얼 오브젝트를 메모리에서 삭제하려면 C++마냥 delete를 사용해서 하는 것이 아니라 레퍼런스를 없앰으로써 가비지 컬렉터가 자동으로 메모리를 회수할 수 있도록 하는 것이 좋다.

언리얼 오브젝트를 사용한다면 위에서 알아봤던 포인터를 사용함에 있어 나오는 문제점을 해결 할 수 있다.

  • Memory Leak : 가비지 컬렉션을 사용하여 해결
  • Dangling Pointer : IsValid()등의 함수를 사용하여 해결
  • Wild Pointer : UPROPERTY 매크로를 사용하면 자동으로 초기화

회수되지 않는 오브젝트

  • UPROPERTY로 참조된 언리얼 오브젝트 (대부분)
  • FGCObject의 AddReferenceObject 함수를 통해 참조를 설정한 언리얼 오브젝트
    (UPROPERTY를 사용하지 못할 때 Ex : C++ 클래스에서 언리얼 오브젝트를 프로퍼티로 가질 때)
  • 루트셋으로 지정된 오브젝트

언리얼 오브젝트 관리 원칙

  • 생성된 어리얼 오브젝트를 유지하기 위해 레퍼런스 참조 방법을 설계할 것
    • 일반 C++ 오브젝트 내의 언리얼 오브젝트 : FGCObject의 상속 후 구현
    • 언리얼 오브젝트 내의 언리얼 오브젝트 : UPROPERTY
      -> UPROPERTY를 붙이지 않는다면 nullptr은 아닌데 IsValid에서는 유효하지 않다고 나온다.
  • 생성된 언리얼 오브젝트는 강제로 지우려 하지 말 것
    • 참조를 끊는다는 생각으로 설계
    • 가비지 컬렉터에게 회수를 부탁할 수는 있음 (ForceGarbageCollection)
    • Destroy라는 함수를 사용할 수 있다. 근데 이것도 회수 플래그를 세우는 것이지 바로 없애는 건 아님
profile
영차 영차

0개의 댓글