잘못된 포인터 사용 결과

메모리 누수(Leak) : delete를 하지 않아 힙에 그대로 남아 있음

댕글링(Dangling) 포인터 : 이미 해제된 주소를 가리키는 포인터

와일드(Wild) 포인터 : 값이 초기화되지 않아 엉뚱한 주소를 가리키는 포인터

 

가비지 컬렉션 시스템

더 이상 사용하지 않는 오브젝트를 자동으로 감지해 메모리를 해제하는 시스템

모든 오브젝트 정보를 모아둔 저장소를 사용해 사용되지 않는 메모리 추적

 

일반적으로 마크-스윕(Mark-Sweep) 방식을 사용

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

 

언리얼에서는 지정된 주기(GCCycle, 기본값 60초)마다 시스템 동작

ForceGarbageCollection 함수로 가비지 컬렉션 시스템을 바로 동작시킬 수는 있음

백그라운드에서 진행하는 작업이 부하가 있어 병렬 처리, 클러스터링 등 기능 탑재

 

클러스터링

프로젝트에서 켜거나 끌 수 있음

관련된 오브젝트들을 하나의 가비지 컬렉션 클러스터에 묶어 오브젝트 각각이 아닌 클러스터 하나만 검사함

일반적으로 클러스터를 사용하면 가비지 컬렉션 퍼포먼스가 향상되지만 클러스터가 너무 클 경우 같은 프레임에 클러스터의 개별 오브젝트 전부를 삭제 준비하므로 버벅일 수 있음

 

GUObjectArray : 관리되는 모든 언리얼 오브젝트의 정보를 저장하는 전역변수

Garbage 플래그 : 참조가 없어 회수 예정인 오브젝트를 나타내는 플래그

RootSet 플래그 : 참조를 파악하기 위해 시작하는 시드 오브젝트를 나타내는 플래그, 참조가 없어도 회수되지 않음

 

시스템이 Garbage 플래그로 설정된 오브젝트를 파악하고 메모리를 안전하게 회수

오브젝트를 삭제하기 위해 레퍼런스 정보를 없애면 가비지 컬렉터가 자동으로 메모리 회수

 

메모리 회수되지 않아야 하는 오브젝트는 AddToRoot 함수로 RootSet 플래그를 설정

RemoveFromRoot 함수로 루트셋 플래그를 제거

컨텐츠 제작에는 권장하지 않음

 

UPROPERTY로 선언한 언리얼 오브젝트는 회수되지 않음

만약 UPROPERTY를 사용할 수 없다면 AddReferencedObject 함수로 참조 설정

오브젝트 포인터는 가급적 UPROPERTY로 선언하여 가비지 컬렉터가 자동으로 관리하도록 함

언리얼 오브젝트를 강제로 지우려 하지 말고 참조를 끊는 방법 사용

Actor를 소멸시키기 위해 Destory 함수 사용, 내부 동작은 가비지 컬렉터와 동일

 

일반 c++ 클래스가 언리얼 오브젝트로 관리해야 하는 경우 FGCObejct 상속, AddReferencedObjects, GetReferenceName 함수 구현해야 함
AddReferencedObjects에서 관리할 언리얼 오브젝트를 추가

class UNREALMEMORY_API FStudentManager : public FGCObject
{
public:
    virtual void AddReferencedObjects(FReferenceCollector& Collector) override;

    virtual FString GetReferenceName() const override
    {
        return TEXT("FStudentManager");
    }
};

 

언리얼 오브젝트는 nullptr 체크만 하면 댕글링 포인터가 발생할 수 있으므로 댕글링 포인터 문제를 탐지하기 위해 IsValid 함수 제공

IsValid는 플래그 기반으로 검사하고, IsValidLowLevel은 엄격하게 객체가 등록됐는지 검사

+ Recent posts

목차