메모리 최적화
Hard/Soft Reference, Asset Manager, 동적 로딩으로 메모리 관리하기
Hard vs Soft Reference
에셋 참조 방식의 이해
부모 로드 시 자동 로드
- UPROPERTY에 직접 참조
- 항상 메모리에 로드됨
- 시작 시 메모리 사용량 증가
- 로딩 시간 증가
필요할 때만 로드
- TSoftObjectPtr 사용
- 경로만 저장, 데이터는 로드 안됨
- 동적 로딩 가능
- 메모리 효율적
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 예제
// Soft Reference: 경로만 저장, 실제 데이터는 로드 안됨
UPROPERTY(EditDefaultsOnly, Category = "Assets")
TSoftObjectPtr<UStaticMesh> LazyLoadedMesh;
UPROPERTY(EditDefaultsOnly, Category = "Assets")
TSoftClassPtr<AActor> LazyLoadedActorClass;
Soft Reference 로딩
동기/비동기 로딩 방법
동기 로딩 (Synchronous)
// 동기 로딩 - 히치(Hitch) 발생 가능!
UStaticMesh* AMyActor::GetMeshSynchronous()
{
if (LazyLoadedMesh.IsPending())
{
// 로드 완료될 때까지 대기 (블로킹)
LazyLoadedMesh.LoadSynchronous();
}
return LazyLoadedMesh.Get();
}
동기 로딩은 게임 스레드를 블로킹합니다. 대용량 에셋은 반드시 비동기 로딩을 사용하세요.
비동기 로딩 (Asynchronous) - 권장
// 비동기 로딩 - 게임 플로우 방해 없음
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);
}
}
Asset Manager 활용
체계적인 에셋 관리
Primary Data Asset 정의
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 로딩
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);
}
메모리 프로파일링
메모리 사용량 분석 도구
콘솔 명령어
// 메모리 리포트
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)
// LLM 활성화 (-llm 커맨드라인 옵션 필요)
stat llm // LLM 통계 표시
stat llmfull // 상세 LLM 통계
// 카테고리별 메모리 확인
// - Meshes
// - Textures
// - Audio
// - Animation
// - Physics
// 코드에서 메모리 태깅
LLM_SCOPE(ELLMTag::Meshes);
// 이 스코프 내 메모리 할당은 Meshes로 추적됨
베스트 프랙티스
메모리 최적화 체크리스트
불필요한 Hard Reference 체인 찾기
텍스처, 메시, 사운드 등
LoadSynchronous 대신 RequestAsyncLoad
UnloadPrimaryAsset으로 메모리 해제
메모리 누수 및 비정상적 사용 감지
핵심 요약
- Hard Reference - 부모 로드 시 함께 로드, 시작 메모리 증가
- Soft Reference - TSoftObjectPtr, 필요할 때만 로드
- 비동기 로딩 - RequestAsyncLoad로 히치 없이 로딩
- Asset Manager - PrimaryDataAsset으로 체계적 관리
- memreport -full - 상세 메모리 리포트 생성
- Reference Viewer - Hard Reference 체인 시각화
World Partition과 함께 Soft Reference를 사용하면 필요한 영역의 에셋만 로드하여 메모리를 효율적으로 관리할 수 있습니다.
도전 과제
배운 내용을 직접 실습해보세요
stat Memory, stat MemoryStaticMesh, stat Streaming 명령으로 RPG 프로젝트의 메모리 사용량을 카테고리별로 분석하세요. 텍스처, 메시, 오디오 중 어디서 가장 많은 메모리를 소비하는지 확인하세요.
텍스처 해상도와 스트리밍 풀 크기를 조정하세요. r.Streaming.PoolSize를 설정하고, LODBias로 원거리 텍스처 해상도를 낮추세요. stat TextureStreaming으로 오버 버짓 여부를 확인하세요.
LLM(Low Level Memory Tracker)을 활성화하여 카테고리별 메모리를 추적하세요. 타겟 플랫폼의 메모리 예산을 설정하고, 예산 초과 시 경고하는 자동화 스크립트를 구현하세요.