PART 7 - 강의 5/8

메모리 최적화

Hard/Soft Reference, Asset Manager, 동적 로딩으로 메모리 관리하기

01

Hard vs Soft Reference

에셋 참조 방식의 이해

Hard Reference

부모 로드 시 자동 로드

  • UPROPERTY에 직접 참조
  • 항상 메모리에 로드됨
  • 시작 시 메모리 사용량 증가
  • 로딩 시간 증가
Soft Reference

필요할 때만 로드

  • TSoftObjectPtr 사용
  • 경로만 저장, 데이터는 로드 안됨
  • 동적 로딩 가능
  • 메모리 효율적

Hard Reference 예제

C++ - Hard Reference
// Hard Reference: 이 액터 로드 시 MyMesh도 함께 로드됨 UPROPERTY(EditDefaultsOnly, Category = "Assets") UStaticMesh* AlwaysLoadedMesh; // ConstructorHelpers도 Hard Reference 생성 AMyActor::AMyActor() { static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFinder( TEXT("/Game/Meshes/MyMesh")); if (MeshFinder.Succeeded()) { MyMesh = MeshFinder.Object; // Hard Reference! } }

Soft Reference 예제

C++ - Soft Reference
// Soft Reference: 경로만 저장, 실제 데이터는 로드 안됨 UPROPERTY(EditDefaultsOnly, Category = "Assets") TSoftObjectPtr<UStaticMesh> LazyLoadedMesh; UPROPERTY(EditDefaultsOnly, Category = "Assets") TSoftClassPtr<AActor> LazyLoadedActorClass;
02

Soft Reference 로딩

동기/비동기 로딩 방법

동기 로딩 (Synchronous)

C++
// 동기 로딩 - 히치(Hitch) 발생 가능! UStaticMesh* AMyActor::GetMeshSynchronous() { if (LazyLoadedMesh.IsPending()) { // 로드 완료될 때까지 대기 (블로킹) LazyLoadedMesh.LoadSynchronous(); } return LazyLoadedMesh.Get(); }
주의

동기 로딩은 게임 스레드를 블로킹합니다. 대용량 에셋은 반드시 비동기 로딩을 사용하세요.

비동기 로딩 (Asynchronous) - 권장

C++
// 비동기 로딩 - 게임 플로우 방해 없음 void AMyActor::LoadMeshAsync() { if (LazyLoadedMesh.IsPending()) { FStreamableManager& Streamable = UAssetManager::GetStreamableManager(); Streamable.RequestAsyncLoad( LazyLoadedMesh.ToSoftObjectPath(), FStreamableDelegate::CreateUObject( this, &AMyActor::OnMeshLoaded) ); } else if (LazyLoadedMesh.IsValid()) { // 이미 로드됨 OnMeshLoaded(); } } void AMyActor::OnMeshLoaded() { if (UStaticMesh* Mesh = LazyLoadedMesh.Get()) { // 로드 완료, 메시 사용 MeshComponent->SetStaticMesh(Mesh); } }
03

Asset Manager 활용

체계적인 에셋 관리

Primary Data Asset 정의

C++
UCLASS() class UItemDataAsset : public UPrimaryDataAsset { GENERATED_BODY() public: virtual FPrimaryAssetId GetPrimaryAssetId() const override { return FPrimaryAssetId("Item", GetFName()); } UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) FText ItemName; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) TSoftObjectPtr<UStaticMesh> ItemMesh; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) TSoftObjectPtr<UTexture2D> ItemIcon; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) TSoftClassPtr<AActor> ItemActorClass; };

Asset Manager 로딩

C++
void AMyActor::LoadItemAsync(FPrimaryAssetId ItemId) { UAssetManager& AssetManager = UAssetManager::Get(); // 로드할 번들 지정 (선택적) TArray<FName> BundlesToLoad; BundlesToLoad.Add(FName("Gameplay")); // 비동기 로드 요청 FStreamableDelegate Delegate = FStreamableDelegate::CreateUObject( this, &AMyActor::OnItemLoaded, ItemId); AssetManager.LoadPrimaryAsset(ItemId, BundlesToLoad, Delegate); } void AMyActor::OnItemLoaded(FPrimaryAssetId ItemId) { UAssetManager& AssetManager = UAssetManager::Get(); UItemDataAsset* Item = AssetManager.GetPrimaryAssetObject<UItemDataAsset>(ItemId); if (Item) { // 아이템 데이터 사용 UseItem(Item); // 메시도 비동기 로드 if (Item->ItemMesh.IsPending()) { // 추가 비동기 로딩... } } } // 사용 후 언로드 void AMyActor::UnloadItem(FPrimaryAssetId ItemId) { UAssetManager& AssetManager = UAssetManager::Get(); AssetManager.UnloadPrimaryAsset(ItemId); }
04

메모리 프로파일링

메모리 사용량 분석 도구

콘솔 명령어

Console Commands
// 메모리 리포트 memreport -full // 상세 메모리 리포트 (파일 저장) // UObject 목록 obj list // 모든 UObject 목록 obj list class=StaticMesh // 특정 클래스만 obj list class=Texture2D // 텍스처 목록 // Reference Viewer (에디터) // 에셋 우클릭 > Reference Viewer // Hard Reference 체인 확인 가능 // Size Map (에디터) // 에셋 우클릭 > Size Map // 에셋과 의존성의 크기 시각화

LLM (Low Level Memory Tracker)

Console Commands
// LLM 활성화 (-llm 커맨드라인 옵션 필요) stat llm // LLM 통계 표시 stat llmfull // 상세 LLM 통계 // 카테고리별 메모리 확인 // - Meshes // - Textures // - Audio // - Animation // - Physics
C++ - 커스텀 LLM 태그
// 코드에서 메모리 태깅 LLM_SCOPE(ELLMTag::Meshes); // 이 스코프 내 메모리 할당은 Meshes로 추적됨
05

베스트 프랙티스

메모리 최적화 체크리스트

1
Reference Viewer로 의존성 분석

불필요한 Hard Reference 체인 찾기

2
대용량 에셋은 Soft Reference 사용

텍스처, 메시, 사운드 등

3
비동기 로딩 활용

LoadSynchronous 대신 RequestAsyncLoad

4
사용 후 언로드

UnloadPrimaryAsset으로 메모리 해제

5
정기적으로 memreport 확인

메모리 누수 및 비정상적 사용 감지

SUMMARY

핵심 요약

핵심 포인트
  • Hard Reference - 부모 로드 시 함께 로드, 시작 메모리 증가
  • Soft Reference - TSoftObjectPtr, 필요할 때만 로드
  • 비동기 로딩 - RequestAsyncLoad로 히치 없이 로딩
  • Asset Manager - PrimaryDataAsset으로 체계적 관리
  • memreport -full - 상세 메모리 리포트 생성
  • Reference Viewer - Hard Reference 체인 시각화
대규모 오픈월드 핵심

World Partition과 함께 Soft Reference를 사용하면 필요한 영역의 에셋만 로드하여 메모리를 효율적으로 관리할 수 있습니다.

PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: 메모리 통계 분석

stat Memory, stat MemoryStaticMesh, stat Streaming 명령으로 RPG 프로젝트의 메모리 사용량을 카테고리별로 분석하세요. 텍스처, 메시, 오디오 중 어디서 가장 많은 메모리를 소비하는지 확인하세요.

실습 2: 텍스처 스트리밍 최적화

텍스처 해상도와 스트리밍 풀 크기를 조정하세요. r.Streaming.PoolSize를 설정하고, LODBias로 원거리 텍스처 해상도를 낮추세요. stat TextureStreaming으로 오버 버짓 여부를 확인하세요.

심화 과제: 메모리 버짓 관리 시스템

LLM(Low Level Memory Tracker)을 활성화하여 카테고리별 메모리를 추적하세요. 타겟 플랫폼의 메모리 예산을 설정하고, 예산 초과 시 경고하는 자동화 스크립트를 구현하세요.