https://www.youtube.com/watch?v=VpEe9DbcZIs
이 글은 해당 영상을 정리한 글입니다.
참조 카운터 방식 가비지 컬렉션
- Raw 포인터 대신 RAII 스타일의 스마트포인터 사용
- std::shared_ptr, 언리얼의 TSharedPtr
- 별도로 할당된 메모리 블록에 객체의 참조 카운터를 업데이트하다
카운터가 0이 되면 메모리를 해제
참조 카운터 방식은 순환 참조에 주의해야 함
- 참조 카운터가 서로 가르켜 0이 되지 못해 메모리 누수가 발생
- 소유권(ower)방향의 참조는 shared_ptr을 사용해 강참조,
그 이외는 weak_ptr을 사용하여 약참조를 해 순환 참조 예방
Mark-Sweep 방식 가비지 컬렉션
가비지 컬렉터 메모리관리를 하기때문에 할당한 객체들의 주소를 전부 알고있다고 가정
객체를 사용하지 않아도 해제하지 않고 놔두다가 필요할 때 정리
- Mark-Sweep 알고리즘의 한 사이클은 Mark 단계와 Sweep 단계로 나누어짐
- Mark
GC Root로 부터 순환을 돌아 참조 되고 있으면 Mark - Sweep
Mark되지 않은 객체를 메모리에서 지움 - Compact
Sweep 단계 후 메모리를 압축
최근 GC에서 추가된 단계
- Mark
- Generational(세대별) GC
- 대부분의 객체는 생성된지 얼마 안돼 접근이 불가능 해짐
즉 오래살아남은 객체가 앞으로도 살아남을 가능성이 높음
객체의 세대를 구분해 GC의 주기를 새로생긴 세대는 자주, 기존 세대는 드물게 GC를 실행
- 대부분의 객체는 생성된지 얼마 안돼 접근이 불가능 해짐
- Stop-the-World : GC 실행중 프로세스(모든 스레드)를 멈춤
준비 단계
- 모든 객체의 마킹 해제
Mark 단계
- Root Set으로부터 도달 가능한 객체 마킹
Sweep 단계
- 프로그램이 도달 불가능한(마킹되지 않은) 객체 해제
Conservative 가비지 컬렉터
- 객체의 구체적인 생김새(정보)를 모르는 채로 동작
- 크기만 주어진 메모리 블록으로 취급
- 메모리 블록 내 어디든 잠재적으로 포인터일 가능성이 존재
- 할당한 객체의 주소가 등장하면 모두 포인터로 간주
즉 객체의 주소 or 주소와 일치하는 정수가 나오면 루트에서 접근 가능한 객체로 인식 - False Positive가 발생할 수 있음
- False Positive
실제로는 객체를 참조하고 있지 않은데 객체의 주소 값이 메모리의 정수 값과 같을경우
해당 객체를 참조하고 있는것으로 착각하여 메모리를 해제하지 않고 누수가 발생
- False Positive
- 할당한 객체의 주소가 등장하면 모두 포인터로 간주
Boehm-Demers-Weiser Garbage Collector
- Conservative GC의 대표적인 GC 주로 C, C++에서 사용됨
- 유니티 엔진에서 사용 중
- Incremental(점진적) GC
Boehm-Demers-Weiser GC는 Stop-the-World 발생시 프레임 드랍이 발생
유니티의 경우 Incremental GC로 GC 작업을 여러 개의 슬라이스로
분할하여 여러번에 걸쳐 짧게 중단되어 프레임 드랍을 최소화시킴
- Incremental(점진적) GC
- 최근 GC는 Mark-Sweep 후 Compact(메모리 압축) 과정을 거치는데
Conservative 방식의 GC는 Compact를 구현할 수 없는 한계가 있음
Precise 가비지 컬렉터
- Conservative GC와는 다르게 객체의 정보를 명확히 알고 동작
- 언리얼 엔진은 리플렉션 정보를 활용해 객체의 참조를 정확하게 알 수 있음
언리얼 엔진의 GC
- UPROPERTY 객체가 해당 필드 내의 모든 UObject 포인터 위치를 GC에게 알려줌
- GC 사이클마다 UPROPERTY를 매번 참조하는게 아닌 자료구조를 활용해 미리 생성하여 참조
- 리플렉션 매크로가 적용되지 않는 경우 UObject::AddReferencedObjects 함수를 통해 GC에게 정보를 알려줄수 있음
TWeakObjectPtr
- 순환참조를 방지하는 TWeakPtr과 다름
- Raw 포인터는 강한 참조로 Raw 포인터가 가리키고 있는 동안은 대상 객체가 해제되지 않음
이를 원치 않는 경우 약한 참조 TWeakObjectPtr를 사용 - 강참조
TObjectPtr, TSharedPtr, 로우포인터 등으로 참조한 인스턴트는 GC가 순회할때 Mark할 수 있음 - 약참조
TWeakObjectPtr은 약참조로 GC가 순회할 때 해당 포인터로 참조하고 있는 인스턴트는 무시하여
Mark를 하지 않음 그래서 약한 참조외 다른 강참조가 없다면 GC의 수집대상이 됨
언리얼 GC의 순회 대상
- GC 루트에 등록된 객체
- UPROPERTY()나 TObject로 참조되는 객체
- 월드, 액터, 컴포넌트 트리 상에 존재하는 객체
위 조건에 만족하지 않으면 GC의 수거 대상이 됨
예를 들어 TWeakObjectPtr<AActor>로 스폰된 액터가 월드에 존재하면
별도의 강참조가 없더라도 GC의 수거 대상이 되지 않음
네이티브 C++ 객체 내에 UObject 포인터가 있을 경우
- FGCObject를 상속하여 AddReferencedObjects를 override하여 UObject 포인터를 GC에게 알려줄 수 있음
- 네이티브 C++객체가 GC에 의해 해제 되는게 아닌 Root Set으로 지정하는 효과
FGCObject를 상속받지 못하는 경우
- Raw 포인터 대신 TStrongObjectPtr 또는 TWeakObjectPtr 포인터 객체를 사용
객체를 강제로 해제
- 도달 가능한 UObject 객체라도 MarkPendingKill 호출로 인해 강제 해제될 수 있음
- 가비지 컬렉터는 해당 객체를 가르키는 모든 포인터 변수의 값을 안전하게 NULL로 업데이트함
추가로 보면 좋은 글
GC에 대해 보다 자세히 설명되어 있음
https://blog.siner.io/2021/12/26/garbage-collection/
가비지컬렉션(Garbage Collection)의 종류와 특징
Garbage란? Garbage는 컴퓨터의 메모리에 있지만 앞으로 사용되지 않을 데이터나 객체 또는 메모리 영역을 가리킵니다. 모든 컴퓨터 시스템은 제한적인 메모리를 가지고 있고, 대부분의 소프트웨어
blog.siner.io
자바의 세대별 GC 동작
☕ 가비지 컬렉션 동작 원리 & GC 종류 💯 총정리
Garbage Collection(GC) 이란? 가비지 컬렉션(Garbage Collection, 이하 GC)은 자바의 메모리 관리 방법 중의 하나로 JVM(자바 가상 머신)의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리 객
inpa.tistory.com
언리얼 GC에 대해 정리된 글
https://husk321.tistory.com/392
[Unreal] 언리얼의 GC에 대해서 알아보자. 아주 간략히
개요 언리얼을 처음 접했을 때 스크립팅 언어로 C++을 사용하니 메모리 관리에 있어서 스마트 포인터를 적극 활용하는 등 여러 조치를 취해야 할 거라고 생각했습니다. 뭐 프로그래머가 직접 다
husk321.tistory.com
'Unreal Engine > Unreal 기초' 카테고리의 다른 글
UnrealEngine 리플렉션 (0) | 2025.03.31 |
---|---|
UnrealEnigne Cpp에서 Level 재시작 (0) | 2025.02.07 |
UnrealEnigne Cpp함수를 블루프린트에서 사용 (0) | 2025.02.07 |
UnrealEnigne 헤드업디스플레이 구현 및 초기화 프로세스 (0) | 2024.12.18 |
UnrealEnigne 행동트리(Behavior Tree) 모델 (0) | 2024.11.05 |