Unity를 하면서 가비지 컬렉션을 처음 알았다.
계속 봐도 이해하기가 좀 어렵지만.. 왜 필요한지 부터 알아보고 차근차근 알아봐야겠다!
목차
- 가비지 컬렉션(GC)이란?
- .NET 가비지 컬렉션
- Unity에서의 가비지 컬렉션
- 가비지 컬렉션 최적화
- 정리
1. 가비지 컬렉션(GC)이란?
가비지 컬렉션은 프로그래밍에서 메모리 관리를 자동으로 수행하는 프로세스를 말합니다. (메모리 자동 관리 기능!)
메모리에 데이터를 저장하고 사용하다보면 이러한 데이터들이 언젠가는 더 이상 사용하지 않게 되는 불필요한 데이터들이 생깁니다.
이런 불필요한 데이터를 가비지라고 부르며, 가비지를 식별하고 메모리에서 제거하는 일을 가비지 컬렉션이라고 합니다.
Unity의 가비지 컬렉션은 .NET의 가비지 컬렉션의 방식을 따르게 되는데 Unity에서 사용하는 언어 C#이 .NET 프레임워크로 작동하기 때문입니다.
2. .NET 가비지 컬렉션
.NET의 가비지 컬렉션은 세대별로 메모리를 관리하는 특징이 있습니다.
오랫동안 메모리에 남아있는 데이터를 식별하고 효율적인 관리를 위해서 세대별 메모리 관리를 사용합니다.
세대별 어떤 기능을 담당하는지 알아보겠습니다.
0세대 (Gen0)
새로 생성된 객체가 속하는 세대입니다.
0세대의 객체들은 주로 생명주기가 짧고 빠르게 생성되었다가 빠르게 가비지가 되는 경우가 많습니다.
그렇기에 0세대는 가비지 컬렉션이 가장 빈번하게 수행되는 곳 입니다.
예: Effect 효과, 잠깐 생성되는 string 등..
1세대 (Gen1)
0세대 GC에서 생존한 객체가 속하는 세대입니다.
주로 게임 시작 시 생성되어 플레이 도중 계속 사용되는 객체들이 해당됩니다.
예: 캐릭터(플레이어, 몬스터, ..), 아이템 등..
2세대 (Gen2)
1세대 GC에서 생존한 객체가 속하는 세대입니다.
게임이 실행되고 한번만 생성되고 게임이 종료될 때까지 사용되는 객체들이 해당됩니다.
예: 게임 정보, 플레이어 정보, 싱글톤의 클래스 인스턴스 등..
0세대 부터 메모리 관리 후 메모리가 부족해지면 1세대도 메모리를 관리하고 그래도 부족하면 2세대도 관리합니다. (0세대->1세대->2세대)
그런데 왜? GC가 알아서 메모리를 관리해줄텐데 우리는 이것을 알아야 할까요?
이유는 유니티에서 사용되는 GC가 완벽하지 않기 때문입니다.
3. Unity에서의 가비지 컬렉션
기본적으로 Unity의 GC는 Stop-the-World 방식의 GC입니다. (.Net 방식)
이는 GC가 수행되는 동안 프로그램을 일시 중지하고 메모리를 정리하는 방식입니다.
물론 메모리를 안전하게 정리하고, 메모리 누수 방지에 효과적이지만 게임 성능에는 부정적인 영향을 주게됩니다.
지금은 어떤지 모르겠지만.. 다른 블로그를 본 결과
.NET GC는 세대별 메모리를 관리하고, 힙 영역에서 SOH(Small Object Help), LOH(Large Object Heap)으로 그분하고 관리되는데 Unity는 다르다고 합니다.
Unity는 세대 구분, SOH, LOH, 메모리 정렬을 못하고 (2019부터 개선 중)
구버전에 사용했던 GC를 그대로 사용하고 있다는 등..
이러한 문제들 때문에 GC 최적화가 꼭 필요하다고 합니다.
또한 Profiler에 팍!팍! 튀는 현상을 스파이크 GC라 합니다. (이것 또한 Unity의 GC 문제,,)
4. 가비지 컬렉션 최적화
Profiler로 GC Alloc 부분을 확인하여 GC가 빈번히 일어나는 곳을 확인합니다.
다음과 같이 조치를 해줍니다.
점진적 GC (Incremental GC)
프로그램을 일시 중지하지 않고 여러 프레임에 걸쳐서 메모리를 정리합니다.
설정이나 코드로 활성화할 수 있습니다.
강제적 GC
좋은 타이밍에 GC를 미리 호출하여 메모리를 정리하는 방법입니다.
예: 씬 전환, UI 활성화 등.. 메모리 사용량이 높은 객체에 하나씩 GC를 넣어줌으로써 강제로 GC를 호출합니다.
Object Pool
객체를 재사용하면 당연히 GC에게도 도움을 줍니다.
컬렉션, 배열 등도 재사용하는 것이 좋습니다.
List 사이즈 초기화
List를 생성할 때 미리 크기를 할당해주면 GC가 발생되지 않습니다.
다른 컬렉션도 똑같이 미리 크기를 할당해주는 연습을 해줍시다!
String Builder
string을 여러개 합치는 것 또한 GC 유발 중 하나입니다.
StringBuilder를 통해 메모리 할당 횟수를 줄일 수 있습니다.
리소스 해제
- 사용하지 않는 리소스는
Resources.UnloadUnsuedAssets()
으로 해제합시다.
- 사용하지 않는 리소스는
5. 정리
가비지 컬렉션을 통해 메모리에 불필요한 데이터를 정리하여 메모리 효율을 높일 수 있습니다.
하지만 Unity에서는 .NET의 GC처럼 사용하지 못하며 그로 인해 GC 최적화를 진행해야 합니다.