UDN
Search public documentation:

MemoryDebuggingKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 홈 > 메모리 시스템 > 메모리 디버깅

메모리 디버깅


메모리 디버깅 개요


이 섹션은 메모리와 관련된 문제들– 특히 메모리 사용을 물리적 메모리의 크기로 제한하는 콘솔에서의 메모리 문제에 어떻게 접근하고 처리할 것인지 이해하는데 도움이 될 것입니다.

메모리 문제는 계획에 없던 경비, 대형 자산, 코드의 버그, 그밖의 상황들로 인해 게임 개발 도중 아주 흔하게 발생합니다.

여기서는 이 상황들 중 여러가지를 처리하는 방법에 대한 기본을 제공합니다만, 더 자세한 것은 플랫폼 특정 이슈를 참고하시기 바랍니다.

단계별 메모리 디버깅


1. 게임을 로드한다

메모리 부족으로 인해 게임을 로드할 수 없는 경우에는 레벨의 최적화 페이지에서 레벨을 최적화하는 방법을 참조하십시오.

한 레벨에서 몇 개의 Static Mesh를 사용할지, 또는 Skeletal Mesh나 애니메이션, 사운드 등 다른 자산은 얼마나 사용할지 등 메모리 사용 지침을 정해두는 것이 좋습니다. 이는 여러분의 게임이 어떤 일들을 하는지, 그리고 무슨 게임인지에 따라 달라집니다.

2. 게임을 실행하고 관찰한다

게임이 실행된다고 해서, 메모리 부족 문제에 부닥칠 염려가 없는 것은 아닙니다. 게임이 얼마나 많은 양의 메모리를 사용하고 있는지, 그리고 지나친 사용을 피하면서 최대 용량까지 얼마나 가용 메모리가 더 남아 있는지 알아야 합니다.

플랫폼 공급자들은 이를 위한 도구를 가지고 있습니다. 아래 의 플랫폼 섹션을 참고하십시요.

3. 메모리 사용을 줄인다 (메모리 부족 없이)

메모리가 부족해지면, 그 원인이 무엇인지 범위를 좁혀 알아보아야 합니다.

다음 것들이 원인이 될 수 있습니다:

  • Static Mesh를 너무 많이 가지고 있는 레벨.
  • 발사체 및 입자를 너무 많이 만들어내는 AI.
  • 메모리를 지나치게 많이 배정하는 코드.

기본적으로 이해할 점들

어디에 얼마만큼의 메모리가 배정되어야 할 것인가에 대한 절대적인 해답은 없습니다.

이는 여러분의 게임, 그리고 그 게임이 어디에 메모리를 사용할 예정인지에 달려 있습니다.

따라서 현재 사용량이 어느 정도인지 알고, 이에 맞도록 예산을 세워야 합니다.

  • 예산: 무엇을 위해 메모리를 사용할지 결정합니다.
  • 모든 자산이 최적화되어 있고 불필요한 참조를 가지고 있지 않도록 확인합니다.
  • 게임이 계속 실행되기에 충분한 버퍼(단편화를 위한)를 가지고 있는지 확인합니다.

참조 케이스

           #define KEEP_TRACK_OF_NOVODEX_ALLOCATIONS 1

참조 케이스


레벨의 최적화

Stats

STAT LEVELS

Stat levels 를 실행하여 몇 개의 레벨이 메모리에 로드되어 있는지 확인하십시오.

  • 초록: 언로드됨
  • 빨강: 로드됨 (로드에 걸린 시간도 함께 표시)
  • 파랑: 언로드되어 가비지 콜렉션 되는 중
  • 노랑: 로드되었지만 보이지 않음
  • 분홍: 로드할 준비중

반드시 로드되어야 할 필요가 없는 레벨은 로드하지 않도록 하십시오. 노랑색 레벨들은 될 수 있으면 로드하지 않거나 로드를 미루어도 될 좋은 대상들입니다. 커다란 스트리밍 레벨은 몇 개로 분할하거나 최적화 하십시오.

STAT MEMORY

Stat Memory 는 레벨에서 자산의 메모리 사용에 대한 기본 윤곽을 제공합니다.

  • 오디오 메모리
  • Novodex 할당
  • 애니메이션
  • 정점 조명
  • StaticMesh의 정점/인덱스
  • SkeletalMesh의 정점/인덱스
  • Decal의 정점/인덱스
  • VertexShader
  • PixelShader
  • 텍스처풀의 사이즈
  • FaceFX

많은 경우 이들 가운데 다수, 즉 Animation, SkeletalMesh, Audio, 그리고 FaceFX 등이 Unreal의 참조 시스템으로 인해 게임 데이터에 서로 관련되어 있습니다. 이 점은 이 문서에서 상세히 설명되어 있습니다.

그밖의 유용한 콘솔 명령어:

  • ListSpawnedActors
  • ListLoadedPackages
  • ListPrecacheMapPackages
  • OBJ BULK

최적화와 관련된 자세한 정보는 레벨의 최적화 페이지를 참고하십시오; 또 stat에 대한 상세한 설명은 Stat 해설 페이지를 참고하십시오.

엔진 환경 설정

텍스처풀의 사이즈:

렌더 타겟을 제외한 모든 텍스처는 텍스처풀을 사용합니다. 여기에는 Lightmap도 포함됩니다 (정점 조명 대신에 Lightmap을 사용하는 것은 메모리 절약에 확실히 더 효과적입니다. Lightmap은 시스템의 메모리를 동요시키지 않기 때문입니다).

또 게임을 위한 텍스처풀의 사이즈가 최적화되어 있는지 확인하십시오. Stat Memory 는 사용되고 있는 텍스처풀의 수를 보여줍니다. 이를 바탕으로 게임에 가장 알맞은 수를 결정할 수 있습니다.

In *Engine.ini,
      [TextureStreaming]
         PoolSize=120

빈번한 가비지 수집:

게임에 가비지 수집 을 더 자주 할 필요가 있다면, 아래의 설정을 사용하시기 바랍니다.

*Engine.ini에서,
      [Engine.Engine]
         TimeBetweenPurgingPendingKillObjects=10 (10초마다)

애니메이션 최적화

게임에서 AnimSet이 참조되면, 이는 AnimSet에 있는 애니메이션을 모두 로드합니다. 이 애니메이션 가운데 일부만 사용할 생각이라면 이것은 낭비입니다. AnimSet 내의 애니메이션이 전부 필요하지 않다면, 이를 몇 개의 하위 AnimSet으로 분할하여 필요할 때만 로드할 것을 권합니다.

예를 들면, 언제나 모든 무기 애니메이션이 필요하지 않은 한 여러가지 무기들이 각기 고유의 AnimSet을 갖도록 하는 것입니다.

Matinee:

Matinee 의 경우 최적화 문제가 더욱 심각해질 수 있습니다.

마티네의 시퀀스마다 또는 레벨마다 다른 AnimSet을 갖는다는 것은 워크플로에 따라서(예:각 Matinee 시퀀스를 각기 다른 사람이 담당하고 있는 경우) 매우 성가신 일이 될 수 있습니다. 레벨마다 하나씩의 AnimSet을 가지게 된다면 이상적이겠지만, 여러 개발자 그룹 (레벨 디자이너/애니메이터/스크립터) 사이에서 이를 관리하는 것은 매우 시간이 걸리는일입니다.

InterpData의 bShouldBakeAndPrune (CL 259515) 플래그는 이러한 경우에 도움이 됩니다. 이는 해당 레벨을 위한 새 AnimSet을 만들어 그 레벨에서 사용되는 애니메이션만을 복제하고, 참조를 다시 링크합니다. 만일 그 AnimSet이 어떤 식으로든 쿡되거나 로드되었다면, 이 플래그는 무효화되어야 합니다. 같은 애니메이션이 복제될 것이기 때문입니다. 이 플래그는 굽거나 손질하고 있는 AnimSet이 레벨에 의해 로드될 필요가 없을 때 사용되어야 합니다. *Editor.ini 의 Cooker.MatineeOptions 섹션에서 bBakeAndPruneDuringCook 를 FALSE 로 설정하여 이 플래그를 글로벌로 무효화할 수 있습니다.

*Editor.ini에서,
      [Cooker.MatineeOptions]
         bBakeAndPruneDuringCook=false

애니메이션 압축:

애니메이션 압축은 애니메이션의 메모리를 크게 절약할 수 있는 부분입니다. 자세한 내용은 애니메이션 압축 페이지를 참고하십시오.

참조 시스템

게임에서 레벨들이 거의 완성되어 가면, 이들을 모두 점검하여 꼭 필요한 자산들만이 로드되었는지 확인하십시오. Unreal Engine 3에서는 참조된 컨텐츠들은 게임에 모두 로드됩니다. 그리고 컨텐츠를 참조하는 직접 및 간접적인 방법들이 아주 많기 때문에, 자신이 깨닫지 못하는 사이에 대량의 자산을 로드하게 될 수 있습니다. 예를 들면 Skeletal Mesh를 참조하는 Pawn은 자동으로 그 Skeletal Mesh에 의해 참조된 AnimSet을 로드하게 됩니다. 따라서 모든 애니메이션(Pawn->Skeletalmesh->Animsets->Animations)도 로드됩니다. 이 연쇄관계는 메모리 면에서 위험할 수 있습니다. 게임이 서로를 참조하는 Actor들을 더 많이 가지게 되기 때문입니다.

여러분은 보통 레벨 디자이너들과 함께 작업하면서 로드되면 안될 것들, 또는 스트리밍 레벨들 간에 분할되어 한꺼번에 로드되면 안되는 것들이 로드되고 있는 것들이 있는지 결정할 것입니다. 해당 레벨에 등장하지 않는 적병의 메쉬가 그 좋은 예입니다.

MEMLEAKCHECK:

Memleakcheck 은 많은 양의 기본적인 게임 데이터를 [ProfileDirectory:Platform-specific]/MemLeak 에 하나의 출력 텍스트 파일로 인쇄합니다.

이는 최소한 Skeletal Mesh, AnimSet, 사운드 등등의 객체 리스트에 대한 스냅샷을 나타냅니다. 기본적인 스냅샷을 얻으면, 이것을 점검하여 불필요하게 로드된 자산이 있는지 확인할 수 있습니다. 불필요하게 로드된 자산이 있다면 그 자산이 누구에 의해 참조되었는지 추적해서 그 참조를 제거하는 것이 해결의 열쇠입니다.

다음은 사례 연구입니다...

OBJ LIST CLASS=SKELETALMESH:

로드된 SkeletalMesh들을 모두 사이즈 순으로 나열합니다. MEMLEAKCHECK 은 이 정보를 포함하고 있지만 알파벳 순으로 되어 있어, 내용을 능률적으로 찾기 어렵습니다.

LISTSOUNDS:

로드된 SoundCue들을 모두 사이즈 순으로 나열합니다. MEMLEAKCHECK 은 이 정보를 포함하고 있지만 이 역시 알파벳 순으로 되어 있어, 내용을 능률적으로 찾기 어렵습니다.

OBJ LIST CLASS=STATICMESH: 로드된 StaticMesh들을 모두 사이즈 순으로 나열합니다.

LISTANIMSETS:

로드된 AnimSet들을 모두 사이즈 순으로 나열합니다. MEMLEAKCHECK 에도 이 정보가 포함되어 있습니다.

OBJ REFS:

자산이 왜 로드되고 있는지 알아내는데 가장 도움이 되는 방법은 OBJ REFS 명령어를 사용하는 것입니다. 이것을 사용하려면 PC에서 게임을 실행해야 합니다. 이는 회귀 함수들로 인해 여러 층의 스택을 요하고, 대개 콘솔 버전의 UE3을 크래시 하기 때문입니다.

문제의 자산이 로드된 곳까지 플레이한 다음, OBJ REFS CLASS=<클래스 이름> NAME=<이름> 을 입력하십시오:

예:

OBJ REFS CLASS=SKELETALMESH NAME=BIG_OGRE_2

그러면 이 자산의 참조 사슬이 나타납니다. 예:

Log: Shortest reachability from root to SkeletalMesh (근원에서 SkeletalMesh 까지의 최단 도달거리) COG_Bolo_Grenade.Frag_Grenade:
Log:    SkeletalMesh COG_Bolo_Grenade.Frag_Grenade [target] (root) (standalone)
Log:    SkeletalMeshComponent GearGame.Default__GearProj_FragGrenade:COGGrenadeMesh0 (root) (ObjectProperty Engine.SkeletalMeshComponent:SkeletalMesh)
Log:    Class GearGame.GearProj_FragGrenade (root) (standalone)

위의 예에서는 Frag_Grenade 메쉬가 native 클래스인 GearProj_FragGrenade의 기본 속성에서 참조되고 있습니다.

참조가 하나 이상일 수 있다는 점을 유의하십시오. 이 경우 참조들을 한 번에 하나씩 처리해야 합니다.

자산이 정확하지 않게 로드되었다고 판정되는 데는 다음 몇 가지 이유가 있습니다:

  • 자산이 native 클래스에 의해 참조되었습니다.
  • UnrealScript 코드가 (그 자산을 참조하는) 클래스를 참조하여 거기서 기본 속성을 가져옵니다:
                      Asset = class'MyGameContent.Pawn_BigOgre'.default.Mesh.PhysicsAsset;

  • Touch Kismet 이벤트(SeqEvt_Touch)가 ClassProximityTypes 또는 IgnoredClassProximityTypes 배열에 클래스를 참조합니다.

메모리 단편화

MEM STAT: 요약에 한함.

MEM DETAILED:

할당자에 관한 유용한 정보를 인쇄합니다. 아래 의 플랫폼 섹션을 참고하십시오.

메모리 단편화를 위한 버퍼를 가지고 있는 것이 좋습니다.

스택 메모리

스택 메모리는 메인 쓰레드에 상속됩니다. 쓰레드를 만들 때 그 사이즈를 지정할 수는 있지만, 내부적으로 쓰레드를 생성하는 라이브러리를 이용하는 경우 이는 메모리를 고려하지 않습니다.

참조


아래의 페이지들은 메모리에 관한 한층 자세한 정보들을 담고 있습니다.

콘솔에서의 메모리


유용한 명령어들