Unity 메모리 관리, GC, 최적화 접근, GC 스파이크, 점진적 GC, Incremental GC

Youngmin Choi·2021년 9월 9일
0

Unity

목록 보기
6/16
post-thumbnail

개발을 하다보면 메모리 관리는 필수불가결한 영역이다.
Unity툴을 이용해서 게임을 제작할 때도 마찬가지이다. 초기에는 구현에만 신경을 썼을지 몰라도 제작하면서 지연되는 시간, 프레임 드랍 등 생각을 해보면 항상 큰 문제가 되는 것이 메모리 부분 혹은 렌더링 부분일 것이다.


Unity 같은 경우에
Mono 프로젝트를 기반으로 돌아간다.
Mono란 놈은 Runtime 시스템에서 자동으로 메모리 관리를 수행한다는 것이 핵심이다!

Q. Mono란?

윈도우 이외의 환경에서도 .Net Framework를 사용하기 위해 만든 프로젝트다. 즉, 다양한 운영체제 환경에서도 멀티플랫폼으로 지원되는 환경을 뜻한다.

1. Unity의 메모리 관리 방식과 GC

  • 메모리를 파악하고 관리하는 프로세스를 가비지 컬렉션(Garbage Collection) 즉 GC라고 보면 된다! Unity에서 GC는 'Boehm-Demers-Weiser' 가비지 컬렉터를 사용한다고 한다.
  • 문제는 GC가 메모리를 수집할 때 기존 메모리 정리 작업도 같이한다.
system.gc.collect();

위와 같은 코드일 것인데 문제가 느리다는 것이다..
'스파이크'라는 단어가 사용될 수준이다. (Unity에서 Profiler를 보면 한 번씩 팍! 튀는 현상)
근데 왜?? Unity GC가 느린건가?!


기존 .Net의 GC를 보면, 메모리가 세대별로 할당돼서 Heap영역은 SOH(Small Object Heap)과 LOH(Large Object Heap)으로 구분돼서 관리된다.
하지만 Unity는 다르다..
SOH, LOH 그딴거 없음..
상당히 구시대적 GC라는 말
(실제로 2019 이전 버전까지는 이러했고, 이후 버전에서 개선되었다고는 하나 만족할 수준인지는 모르겠음.)

Q. 그렇다면 Unity GC는 왜이런 것일까?

  • 간단히 말해서 Microsoft가 Xamarin 인수 후 Mono의 핵심 자산을 제어하기 시작했다. 그 결과 게임 엔진에 통합하기 시작했고 아직 개발이 진행중이라고 한다. (후딱 됬으면..)

Q. 얼마나 느린데..?

  • 개발사에서 공유한 메뉴얼에 따르면 Heap에 100m가 할당되어 있다고 할 때, GC가 작동하면 70ms이다. 500m가 할당되었다면 350ms를 소요한다.
    0.3~0.4초 정도..
    게임이라면 바로 삭제사유 가능! 뚝뚝 끊김..
    프레임이 확실히 튀어 보인다.
    그리고 기본적으로 메모리 사용량이 한계에 따르면 정리를 해야 해서 불리는데 이 뿐만 아니라
    구동하고 있는 환경/플랫폼, 어느 어플 메모리를 같이 동작중인 것인지, GC할당 빈도 등등 다양한 요소가 GC Collect를 호출한다는 것이다.

Q. Unity 최적화를 위해서 어떻게 해야할까?

  • 일단 당연히 Profiler를 본다.
    Profiler에 'GC Alloc' 부분을 확인할 수 있는데 GC가 먹고 있는 양을 뜻한다.
    얼마나 부하인지 체크 가능!
    2019 이상 버전일 경우
    Project Setting -> Player -> Other Seeting -> Configuration 파트에 Use incremental GC (Experimental)를 체크해주자!
GarbageCollector.GCMode = GarbageCollector.Mode.Enabled;
GarbageCollector.GCMode = GarbageCollector.Mode.Disabled;
// 코드로도 가능 //

부하가 있는 GC를 점진적으로 진행시킨다.
즉, 하나의 작업을 여러 프레임으로 나눠서 하는 작업이라고 이해하면 될 것 같다.
Profiler를 확인해보면 확실히 GC 스파이크 문제점을 개선시킬 수 있음!

  • 유니티 메뉴얼에서 권장하는 내용인데, 작은 힙과 빠르고 빈번한 가비지 컬렉터이다. 가비지 컬렉션을 일정 프레임 간격마다 주기적으로 요청한다는 것이다. 이 방법은 오래 플레이 되는 게임에서 부드러운 프레임률을 유지하는데 가장 적합하다고 한다.
if(Time.frameCount % 30 == 0)
{
    System.GC.Collect();
}

실제로 위 방법이 가비지 컬렉션이 필요 이상으로 빈번하게 발생하게 되지만, 더 빠르게 수행되어 게임플레이에 최소한의 지장을 주게 된다! (근데 좋은건지는 잘 모르겠음..)

  • 강제적인 가비지 컬렉션 방법이다. 가장 무난한건데, 좋은 타이밍에 GC를 미리 호출하는 것을 생각해볼 수 있다. 특정 위치 예를 들어, 씬전환 / UI전환 / 특정 이벤트 등 뭔가 로딩이 있을 경우 끼워 넣어주면 좋겠다.

  • 게임에서 자주 사용하는 개체는 해제보다는 재사용하는 편이 GC에 좋으므로 오브젝트 풀을 활용하자!
  • Resources.UnloadUnsuedAsset(); 으로 사용하지 않는 리소스를 해제하자!
profile
Always, Continually, In all circumstance

0개의 댓글