PART 6 · 강의 2/4

대량 객체 생성/파괴

Batch Spawning, Deferred Destruction, GC 트리거 제어로 대량 객체를 효율적으로 관리합니다

SECTION 01

Batch Spawning 전략

대량 객체를 프레임 분산하여 생성하는 기법

프레임 분산 스폰

C++ // 한 프레임에 1000개 → 히치! // for (int i = 0; i < 1000; i++) SpawnActor(...); // 프레임 분산 스폰 void ASpawnManager::StartBatchSpawn(int32 Total, int32 PerFrame) { PendingSpawnCount = Total; SpawnPerFrame = PerFrame; } void ASpawnManager::Tick(float DeltaTime) { if (PendingSpawnCount <= 0) return; int32 ToSpawn = FMath::Min(SpawnPerFrame, PendingSpawnCount); for (int32 i = 0; i < ToSpawn; i++) { SpawnSingleActor(); PendingSpawnCount--; } } // 결과: 1000개를 20프레임에 걸쳐 50개씩 생성 // 프레임당 GC 부하 = 1/20
SECTION 02

Deferred Destruction

대량 파괴를 프레임에 분산하는 지연 파괴 패턴

지연 파괴 큐

C++ UCLASS() class ADestroyManager : public AActor { GENERATED_BODY() UPROPERTY() TArray<AActor*> DestroyQueue; int32 DestroyPerFrame = 10; public: void QueueDestroy(AActor* Actor) { // 즉시 파괴하지 않고 큐에 추가 Actor->SetActorHiddenInGame(true); Actor->SetActorEnableCollision(false); DestroyQueue.Add(Actor); } virtual void Tick(float DeltaTime) override { int32 Count = FMath::Min(DestroyPerFrame, DestroyQueue.Num()); for (int32 i = 0; i < Count; i++) { AActor* Actor = DestroyQueue[0]; DestroyQueue.RemoveAt(0); if (Actor) { Actor->Destroy(); } } } };
GC 트리거 타이밍 제어

대량 파괴 후 즉시 GC가 실행되면 히치가 발생합니다. DelayGarbageCollection()으로 파괴를 모두 완료한 후 안전한 시점(로딩 스크린 등)에 GC를 실행하세요.

SECTION 03

GC 트리거 제어

프로그래밍적 GC 실행 타이밍 제어

전략적 GC 호출

C++ // 레벨 전환 시 GC 패턴 void AMyGameMode::TransitionToLevel(FName NewLevel) { // 1. GC 지연 시작 GEngine->DelayGarbageCollection(); // 2. 현재 레벨 정리 CleanupCurrentLevel(); // 3. 로딩 스크린 표시 ShowLoadingScreen(); // 4. 강제 Full GC (로딩 스크린 중 - 히치 무관) CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS, true); // 5. 새 레벨 로드 UGameplayStatics::OpenLevel(this, NewLevel); } // 웨이브 사이 GC 패턴 (PvE 게임) void AWaveManager::OnWaveCompleted() { // 웨이브 간 전환 연출 중 GC CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS); PrepareNextWave(); }
SECTION 04

Mass Entity 대안

UObject가 아닌 경량 엔티티로 대량 객체 관리

GC를 피하는 설계

수천~수만 개의 유사 객체(군중, 탄막 등)는 UObject/AActor로 만들면 GC 부하가 심합니다. GC를 완전히 피하는 대안을 고려하세요.

대안 기법 // 대안 1: 구조체 배열 (GC 대상 아님) USTRUCT() struct FProjectileData { GENERATED_BODY() FVector Location; FVector Velocity; float Damage; float Lifetime; }; UPROPERTY() TArray<FProjectileData> Projectiles; // 10000개도 GC 부하 0 // 대안 2: Mass Entity Framework (UE 5.0+) // ECS 기반의 경량 엔티티 시스템 // 수만 개의 엔티티를 GC 없이 관리 // 대안 3: Instanced Static Mesh // 시각적으로만 필요한 대량 객체 UInstancedStaticMeshComponent* ISMC; ISMC->AddInstance(FTransform(Location)); // 1개의 컴포넌트로 수천 개 렌더링
GC 비용 비교

10,000개 객체 기준: UObject 사용 시 GC Mark만 ~5ms, 구조체 배열은 GC 비용 0ms. 대량 동일 객체는 반드시 GC를 피하는 설계를 먼저 고려하세요.

SUMMARY

핵심 요약

이 강의에서 배운 내용
  • Batch Spawning으로 대량 생성을 여러 프레임에 분산합니다
  • Deferred Destruction으로 대량 파괴를 프레임당 제한된 수만 처리합니다
  • 레벨 전환 등 안전한 시점에 전략적으로 GC를 호출합니다
  • 수천 개 이상의 유사 객체는 구조체 배열이나 Mass Entity를 사용하여 GC를 피합니다
  • Instanced Static Mesh로 시각적 대량 객체를 효율적으로 처리합니다
PRACTICE

도전 과제

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

실습 1: 대량 UObject 생성/삭제 벤치마크

1,000 / 10,000 / 100,000개의 UObject를 생성하고 삭제하면서 GC 소요 시간, 메모리 사용량, 프레임 히치를 측정하세요. 각 규모에서의 병목 지점(Mark vs Sweep vs Destroy)을 식별하세요.

실습 2: FStructOnScope로 GC 부하 회피

대량의 데이터 객체가 필요한 경우, UObject 대신 FStructOnScope 또는 일반 USTRUCT를 사용하여 GC 추적 오버헤드를 회피하는 패턴을 구현하세요. UObject 버전과 메모리/성능을 비교하세요.

심화 과제: Subsystem 기반 대량 객체 관리 시스템

UWorldSubsystem을 기반으로 대량의 게임플레이 객체(아이템, NPC 상태 등)를 효율적으로 관리하는 시스템을 설계하세요. USTRUCT 데이터 + Handle 패턴으로 GC 부하를 최소화하면서 UObject의 편의성을 유지하는 아키텍처를 구현하세요.