- 순서
- 스택 메모리
- 힙(동적) 메모리
- Dangling Pointer
- Memory Leak
- 스마트 포인터
- 얕은 복사와 깊은 복사
- 언리얼 엔진의 메모리 관리
- 스택 메모리
- 함수 내에서 선언된 지역 변수와 매개변수는 별도로 동적 할당하지 않는 한, 기본적으로 스택 메모리에 저장되며, 함수가 끝나면 자동으로 해제됨.
- 예외적인 상황으로 함수 내의 제어구조, 블록, 스코프 등에서 선언된 변수는 해당 지역이 끝날 때 마다 소멸됨.
- 일반적으로 할당 가능한 스택 메모리의 크기가 제한적임
- 변수의 스코프(생존 영역)을 벗어나면 자동으로 해제되므로, 메모리를 더 길거나 유연하게 관리하기 어려움 => 해당 문제를 해결하기 위해 힙(동적)메모리를 사용할 수 있음
- 힙 메모리
- 동적 할당 시 new 연산자를 사용하고, 해제 시 delete 연산자를 사용함
- 스택과 달리 자동으로 해제되지 않으므로 메모리 누수 등의 위험이 있을 수 있음
- 동적 할당된 객체 또는 변수의 생존 주기는 사용자가 delete로 해제할 때까지 유지됨.
- Dangling Pointer
- 더 이상 유효하지 않은(이미 해제된) 메모리를 가리키고 있는 포인터
- 포인터는 메모리가 해제되었는지 여부를 자동으로 알 수 없기 때문에 발생하는 문제.
- 해당 상태에서 포인터를 역참조 하게되면 예측 불가한 문제가 발생할 수 있음.
- Memory Leak
- 메모리 누수현상.
- 동적으로 할당한 메모리를 사용한 후 제대로 해제하지 않으면, 계속해서 사용하지 않는 메모리가 쌓이게 되는데, 프로그램이 점점 더 많은 메모리를 차지하게 되어 결국 사용할 수 있는 메모리가 부족해지게 된다. 해당 현상을 메모리 누수현상 Memory Leak이라고 한다.
- 스마트 포인터
- unique_ptr(유니크 포인터)
- 단일 소유권을 관리함
- 객체에 대한 소유권을 다른 포인터에 이전할 수 있지만 여러개의 포인터가 동일한 객체를 소유할 수 없다.
- 소유권의 개념만 있기 때문에 복사 혹은 대입이 불가능함.
- 복사가 불가능하여 move를 사용해서 소유권 이전만 가능함.
- shared_ptr(쉐어드 포인터)
- 내부적으로 레퍼런스 카운트를 관리함 - 레퍼런스 카운트는 현재 객체를 참조하는 포인터의 개수를 카운팅 하는 것
- 여러개의 포인터로 하나의 객체를 소유할 수 있다.
- 레퍼런스 카운트가 0이 되면 자동으로 메모리가 해제되어 DanglingPointer나 Memory Leak 문제를 효과적으로 방지 가능하다.
- use_count() 메서드를 활용하여 현재 객체를 참조하는 포인터의 수를 확인할 수 있음.
- reset() 메서드로 소유 중인 객체를 해제하거나 다른 객체로 변경할 수 있음.
- weak_ptr( 포인터)
- 객체의 소유권을 공유하지 않음.
- 레퍼런스 카운트를 증가시키지 않는 약한 참조를 하기 때문에 lock() 호출 후 반환된 shared_ptr이 유효한지 확인 후에 사용해야함.
- weak_ptr은 레퍼런스 카운트에 영향을 미치지 않기 때문에 반드시 lock()함수로 내부 객체 유효성을 확인하고 사용해야한다.
- 순환참조가 발생함
- 순환참조: 두 개 이상의 객체가 서로를 shared_ptr로 가리켜 참조하는 상황 => 메모리 누수가 발생할 수 있음. 서로가 서로를 가리키고 있기 때문에 레퍼런스 카운트가 0이 안될 수 있기 때문
- 순환참조가 발생하지 않도록 순환하고 있는 shared_ptr중 하나를 weak_ptr로 대체하면 순환고리가 끊어져 문제를 해결할 수 있음
- shared_ptr은 관찰과 소유를 하는 반면 weak_ptr은 관찰만 한다고 표현함.
- unique_ptr(유니크 포인터)
- 얕은 복사와 깊은 복사
- 일반적으로 포인터나 동적으로 할당된 자원을 관리하는 객체는 메모리 안정성을 위해 깊은 복사를 사용하는 것이 바람직함.
- 얕은 복사
- 클래스 내의 포인터 멤버를 복사할 때 포인터가 가리키는 데이터가 아닌 포인터가 저장하고 있는 주소값만 복사하는 것을 의미함
- 두 객체가 동일한 동적 메모리 영역을 가리키게 됨. 얕은복사를 수행한 후 원본 객체가 메모리를 해제하면, 복사된 객체의 포인터는 해제된 메모리 영역을 카리게 되므로 dangling pointer가 발생할 수 있음.
- 깊은 복사
- 클래스의 포인터 멤버가 가리키는 동적 데이터를 새로 할당된 독립적은 메모리 영역에 복제하는 것을 의미함.
- 원본 객체와 복사된 객체는 서로 독립적인 메모리 공간을 소유하므로 dangling pointer가 발생하지 않음.
- 언리얼엔진의 메모리 관리
- 가비지 컬렉션(GC)
- 가비지 컬렉션은 언리얼 엔진에서 객체들의 메모리 관리를 자동화하기 위해 사용함
- 장점: 개발자가 메모리 해제를 수동으로 처리하는 부담을 덜고, 메모리 누수나 dangling pointer와 같은 메모리 오류를 줄일 수 있음
- 마크 앤 스윕 알고리즘 방식으로 동작함 - 주기적으로 실행되며, 더 이상 프로그램에서 사용하지 않는다고 판단되는 UObject들을 식별하여 메모리에서 제거함
- 마크 앤 스윕 알고리즘 작동방식 3단계
- 루트셋에서 시작
- 루트셋에 포함된 객체들을 식별 - 해당 객체들은 항상 살아있다고 간주되는 특별한 객체 ex)게임엔진, 플레이어 컨트롤러 등
- 마크 단계 - 도달 가능성 분석
- 루트셋 객체에서 시작해서 직간접적으로 참조하는 UObject를 마크 - 객체가 사용중임을 나타냄
- 스윕 단계 - 메모리 회수
- 마크 단계가 완료되면 마크되지 않은 객체들이 차지하고 있던 메모리를 회수 - 이 과정에서 해당 객체의 소멸자가 호출되고 메모리가 반환

- 마크 단계가 완료되면 마크되지 않은 객체들이 차지하고 있던 메모리를 회수 - 이 과정에서 해당 객체의 소멸자가 호출되고 메모리가 반환
- 루트셋에서 시작
- UObject
- UObject에는 GC 동작 방식을 제어하는 다양한 플래그가 존재함
- 해당 플래그들은 GC의 동작에 중요한 정보를 제공하며, GUObjectArray라는 전역 배열에 저장된 각 객체 정보의 일부로 관리됨.
- RF_RootSet: 해당 플래그가 설정된 객체는 루트셋의 일부로 관리함, 즉 설정된 시점부터 가비지 컬렉션 대상이 아님. AddToRoot() 함수를 통해 설정하고, RemoveFromRoot()함수를 통해 해제할 수 있음
- RF_BeginDestroyed: 객체의 BeginDestroy() 함수가 호출되었음을 나타냄. 객체가 실제로 메모리에서 해제되기 전에 필요한 정리 작업을 수행하는 함수.
- RF_FinishedDestroyed: 객체의 FinishedDestroy() 함수가 호출되었음을 나타냄. 해당 함수는 객체 소멸의 마지막 단계로, 이 함수 호출 후 객체의 메모리가 완전히 배제됨
- 리플렉션 시스템
- 리플렉션: 프로그램이 실행 중에 자신의 구조와 상태를 검사하고 수정할 수 있는 능력
- C++의 경우 자체적인 리플렉션 기능이 없기 때문에 언리얼 엔진은 자체적인 리플렉션 시스템을 구축함
- 리플렉션은 UObject를 위한 운영체제와 같기 때문에 언리얼 엔진 내부에서 동작하는 여러 모듈(GC, script system)은 모두 UObject 기반임.
- 사용자가 정의한 타입들의 경우 엔진에서 알지 못하므로, 이를 처리할 수 있도록 타입 정보를 공유해야하는데 이를 위한 작업이 리플렉션이라고 함.

- 핵심: UHT 코드 생성기
- UHT는 C++ 컴파일러가 수행되기 전에 동작함. C++ 코드 내에서 메타 데이터를 얻고, 내부적으로 소스 코드를 생성함
- 이 동작이 완료된 이후에 C++ 컴파일러가 수행됨.

- 핵심 리플렉션 매크로
- 가비지 컬렉션(GC)
| 매크로 | 리플렉션에서의 목적 | 일반적인 위치 |
| UCLASS() | C++ 클래스를 UObject 기반의 리플렉션 시스템에 등록 | 클래스 정의 앞 |
| UPROPERTY() | 멤버 변수를 리플렉션 시스템에 노출 | 멤버 변수 선언 앞 |
| UFUNCTION() | 멤버 함수를 리플렉션 시스템에 노출 | 멤버 함수 선언 앞 |
| USTRUCT() | C++ 구조체를 리플렉션 시스템에 등록 | 구조체 정의 앞 |
| GENERATED_BODY() | UHT가 생성하는 리플렉션 및 엔진 지원 코드를 위한 삽입 지점 | 클래스/구조체 본문 첫 줄 |

'C++' 카테고리의 다른 글
| [C++]STL(Standard Template Library) (1) | 2025.06.11 |
|---|---|
| [C++]템플릿 (0) | 2025.06.09 |
| [C++]프로그래밍 기초3 (0) | 2025.06.02 |
| [C++]프로그래밍 기초2 (0) | 2025.05.29 |
| [C++]프로그래밍 기초1 (0) | 2025.05.28 |