UObject 선언 기본 원칙
언리얼 오브젝트 포인터 : UPROPERTY 선언
메모리 : 가지비컬렉터가 자동으로 관리하도록 위임
언리얼 엔진 자동 메모리 관리
C++ 메모리 관리 문제점
: 저수준으로 메모리 주소에 직접 접근하는 포인터 사용
- 포인터에 대한 new(할당), delete(해지)를 직접 해줘야함(안하면 이제..오류 투성이 됨)
- 근데 문제는 이걸 잘 못하면? 상당한 문제가 발생(프로그램 자체를 종료)
- 메모리 누수 : new하고 delete를 안해서 heap에 그대로 남아있음, 메모리 낭비 (도서관에 책빌리고 반납안함)
- 댕글링 포인터 (허상포인터) : 이미 delete된 주소를 가리키는 경우 (책반납했는데 책 찾고 있음)
- 와일드 포인터 : 포인터를 초기화되지 않아서 이상한 주소를 가리킴
C++에서 사용하는 메모리 접근 방식(포인터)은 리스크가 너무 크기 때문에 모던 객체지향 언어(Java, C#)은
포인터를 버리고 가비지 컬렉션 시스템을 사용하게 된다.
가비지 컬렉션 GC
: 프로그램에서 더이상 사용하지 않는다고 생각하면, 알아서 메모리를 회수하는 시스템 (자동으로 delete를 해준다)
- 작동 원리 : 동적으로 생성된 모든 오브젝트의 정보를 모아둔 저장소를 사용하여 사용되지 않는 메모리를 추적.
- 대표적인 방식 : 마크 스윕
- 저장소에서 최초 검색을 시작하는 루트 오브젝트를 표시
- 루트 오브젝트가 참조하는 객체를 찾아 마크(Mark)
- 마크된 객체로부터 다시 참조하는 객체를 찾아 마크하고 이를 계속 반복
- 이제 저장소에는 마크된 객체와 마크되지 않은 객체의 두 그룹으로 나뉨
- 가비지 컬렉터가 저장소에서 마크되지 않은 객체들의 메모리를 회수한다. (Sweep)
언리얼엔진 가비지 컬렉션
언리얼 GC 장점
- 메모리 누수 해결
- 언리얼 오브젝트 : GC를 통해서 자동으로 삭제됨
- C++ 오브젝트 : 직접 신경 써야 한다. (스마트 포인터 사용)
- 댕글링 포인터 문제 (오브젝트의 포인터가 외부로부터 해지되어서 유효한지 아닌지를 파악해야함)
- 언리얼 오브젝트 : ::IsValid() 함수로 파악
- C++ 오브젝트 : 직접 신경 써야 한다. (스마트 포인터 사용)
- 와일드 포인터 문제
- 언리얼 오브젝트 : UPROPERTY를 지정하면, nullptr로 초기화해줌
- C++ 오브젝트 : 손수 초기화(nullptr) 또는 스마트 포인터 사용
※ 스마트 포인터
: C++ STL에서 제공하는 스마트 포인터는 기존 포인터의 부족한 점인 메모리와 성능의 관점에서 최대한 효율성을 보장하는 포인터를 포함하는 클래스
- 일반적으로 new를 통해 생성하고, delete를 통해 반환하지만, 프로그램이 방대해질수록 실수가 발생하기에 이를 방지
- 스마트 포인터는 기본적으로 메모리 사용이 끝나면 자동으로 해제
- unique_ptr : 기본 포인터로 한 명의 소유자만 허용, 특정 객체를 소유하는 방식
소유자 이동 O, 복사 및 공유 X - shard_ptr : 참조의 횟수 계산, 일반적인 포인터를 여러 사용자가 공유할 때 사용
참조 횟수가 없으면 삭제(GC와 비슷) - weak_ptr : shared_ptr에 대한 엑세스, 하지만 참조 계산은 하지 않는다.
설정
언리얼 엔진도 지정된 주기마다 몰아서 없애는(60초) 마크스윕 방식으로 GC를 구성한다.
가비지 컬렉션도 일종의 프로그램이라서 동작부하가 생기는데 이를 위해 병렬처리 클러스터링 같은 기능이 있다.
이러한 기능은 ProjectSetting에서 GC를 설정할 수 있다.
GC를 위한 객체 저장소
: 언리얼 엔진에서 관리되는 모든 UObject의 정보를 저장하는 전역 변수 GUObjectArray
GUObjectArray에 저장된 각각의 데이터에는 Flag가 설정되어있고, 이 Flag를 보고 해당 오브젝트의 삭제여부를 정한다.
- Garbage flag : 참조가 없어서 회수 예정인 오브젝트
- RootSet flag : 최초의 오브젝트, 시드오브젝트로, 참조가 없어도! 회수하지 않는 특별한 오브젝트 (매니저 등등)
GC의 메모리 회수법 ( Garbage flag )
- GC는 지정된 시간에 따라 주기적으로 메모리를 설정하는데, 기본은 60초, projectsetting에서 변경도 가능하다.
- 메모리 회수 대상은 Flag가 Garbage인 오브젝트만 회수하게 된다.
그리고 이 flag는 시스템이 설정하기 때문에 프로그래머가 일일이 접근해서 플래그 변경을 할 필요가 없다.
따라서, 한 번 생성된 UObject는 바로 삭제가 되지 않는다.(잘못만들어도 기다려야함) C++처럼 delete를 사용하면 안되고, 레퍼런스 정보를 없애서 자동으로 회수하는 방식으로 진행된다.
GC로 회수되지 않는 UObject
1. 참조를 설정한 UObject
- UPROPERTY 로 참조한 UObject → 대부분 이거 사용
- AddReferencedObject()로 참조한 UObject
UPROPERTY를 사용하지 못하는 경우로, 일반 클래스(c++클래스)에서 언리얼 오브젝트를 관리하는 경우이다.
(일반 클래스에 언리얼 오브젝트가 멤버변수로 들어간 경우)
이런 경우 클래스에서 FGCObject클래스를 상속받은 후, AddReferencedObject()를 구현하고, 구현부에 언리얼 오브젝트를 추가하면 된다.
2. RootSet flag 설정한 UObject
- AddToRoot()를 이용해서 RootSet flag를 설정할 수 있다. (이러면 최초 탐색 목록으로 설정되어 삭제되지 않는다.)
만약 RootSet Flag인 오브젝트를 삭제하고 싶은 경우, RemoveFromRoot()를 이용해서 RootSet flag를삭제할 수 있다.
언리얼 오브젝트 관리 원칙
- 생성된 언리얼 오브젝트를 유지하기 위해서 레퍼런스 참조 방법을 설계
- 언리얼 오브젝트 안에 언리얼 오브젝트라면?
UPROPERTY 사용 - 일반 C++ 오브젝트 안에 언리얼 오브젝트라면?
FGCObject 상속후 AddReferencedObjects() 선언
- 언리얼 오브젝트 안에 언리얼 오브젝트라면?
- 생성된 언리얼 오브젝트는 강제로 지우지 말 것 : 생성하고 GC에게 맞긴다. = 참조를 끊는다.
- GC에게 회수를 재촉할 수는 있음 (ForceGarbageCollection())
- 콘텐츠에서 Actor를 Destroy는 할 수 있지만, 바로 삭제는 안된다.
(결국 GC가 삭제하고, 플래그 설정 후 회수하게끔 동작한다.)
실습
플레이 버튼을 누를 때Init(실행될 때 호출)으로 오브젝트를 생성하고,
GCCycle을 3초로 설정하여 3초이상 대기해 GC를 발동한다. 그 후,
플레이 중지를 눌러 ShutDown(종료될 때 호출)에서 Log로 오브젝트의 유효성을 확인한다.
각각 단일 언리얼 오브젝트, 자료구조(TArray)에 넣은 언리얼 오브젝트, 일반C++ 오브젝트이다.
이렇게 오브젝트 생성도 해줌
(FStudentManager은 StudentManager인데, 이게 일반 C++ 클래스여서 접두사 F를 붙여줌)
CheckUObjectIsValid() : 현재 오브젝트가 유효한지 검사
CheckUObjectIsNull() : 현재 오브젝트가 nullptr인지 검사
delete를 사용해서 c++ 객체를 소멸하는데, 이렇게 선언만 하면,
댕글링 포인터 문제가 발생한다. (객체를 선언하고, 포인터까지 잘 받았는데, ShutDown에서 갑자기 객체를 해제하면, 포인터를 받고 있던 변수 StudentInManager은 사라진 객체의 주소값, 즉 이상한 값을 가지고 있게 된다.)
그래서 아까 말했던 FGCObject 상속후 AddReferencedObjects() 선언하는 과정을 FStudentManager에서 해주어야 한다.
(이렇게 되면 일반적인 C++ 오브젝트도 GC의 관리를 받을 수 있기때문.)
AddReferencedObjects : 저수준에서 오브젝트가 존제한다면, Collector가 관리하겠다
이렇게 까지 추가 구현을 완료하면,
StudentInManger가 유효한 것을 볼 수 있다.
'하고싶은거 > Unreal' 카테고리의 다른 글
Part1 14. 언리얼 오브젝트 관리 : 패키지, 애셋 (0) | 2024.03.16 |
---|---|
Part1 13. 언리얼 오브젝트 관리 : 직렬화 (0) | 2024.03.14 |
Part1 11. 언리얼 컨테이너 라이브러리 UCL : UStruct, TMap (0) | 2024.03.11 |
Part1 10. 언리얼 컨테이너 라이브러리 UCL : TArray, TSet (1) | 2024.03.11 |
Part1 09. 언리얼 C++ 설계 : 델리게이트 (0) | 2024.03.10 |
#개발 #게임 #일상
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!