C#의 object, Unity의 Object의 차이
1. C#의 object
C#에서의 object는 모든 데이터 타입의 최상위 클래스이다. object로 선언된 객체는 .NET의 기본 가비지 컬렉션(GC) 시스템에 의해 관리되며 객체가 더 이상 참조되지 않으면 GC가 자동으로 메모리를 해제한다. 이 과정은 C#의 기본적인 메모리 관리 방식으로, 메모리 관리를 프로그래머가 직접 할 필요가 없다. 하지만 가비지 컬렉션은 일정한 간격으로 실행되므로, 이 시점에서의 성능 저하가 발생할 수 있다.
+ 성능 저하가 일어나는 이유와 발생 주기 및 조건에 대해선 다음에 GC에 대해서 정리하면서 설명할 예정이다. 아래는 이를 간단하게 설명한 것이다.
미리 간단하게 설명하자면, 2가지 이유로 성능 저하가 일어난다.
첫째, GC는 3세대(0, 1, 2 세대)로 관리되는데, 0,1세대는 백그라운드 스레드에서, 2세대는 모든 스레드를 멈추고(이를 StopTheWorld, 이하 STW라 한다.) 힙의 루트 검사와 압축, 마킹 과정을 진행한다. 일부 과정은 STW를 생략하지만 마지막엔 STW가 발생하기에 성능 저하가 있다.
둘째, 유니티의 메모리 할당을 관리하는 힙 매니저는 thread-safe한 기능을 위해 내부적으로 lock을 걸고 메모리를 할당하게 되는데, 이로인해 작업이 직렬화되어 성능 저하가 발생한다.
해결책은 오브젝트 풀을 사용해 동적할당과 해제를 줄이고, 만약 할당할 일이 있을 경우 NativeContainer 또는 Burst Compiler를 사용해서 GC의 영향을 받지 않는 메모리를 할당받아서 사용하는 것이 좋다.
GC가 작동되면 해당 프레임 실행도중 모든 스레드를 멈추기에 사용 자체를 안하는게 제일 좋다. 결국은 C++ 수준에서의 메모리 관리가 필요하다. 그런데 C++이라고 해서 lock이 걸리지 않는 것은 아니다. 가장 원시적인 메모리 할당 방식인 virtualalloc은 thread-safe하지 않으므로 이를 래핑한 heapAlloc을 사용하게 되는데, 이는 윈도우 운영체제가 힙 관리자를 통해 메모리를 할당한다는 의미이다. 즉, 내부적으로 lock을 걸고 작업한다는 의미이며, 작업의 직렬화가 일어난다는 의미이다. 이는 멀티스레드 환경에서 발생하는 오버헤드의 주 원인이 된다.
결론으로, 가장 좋은 방식은 [ 모든 객체를 오브젝트 풀로서 관리하는 것이고, 이외에는 GC가 발동되지 않도록 native 방식(c++)으로 메모리를 관리하는 것 ]이다.
자세한 내용은 다른 포스트에서 다룰 예정이다.
2. Unity의 UnityEngine.Object
Unity의 UnityEngine.Object는 Unity 엔진이 별도로 관리하는 객체이다. Unity에서는 기본 C# GC와 별개로 엔진 자체적인 메모리 관리 시스템을 사용한다. UnityEngine.Object의 경우, 객체가 더 이상 필요 없어지면 Unity 엔진의 메모리 관리 시스템에 의해 수동으로 메모리 해제가 이루어지며, 이 객체는 C# GC의 영향을 받지 않고, Destroy()와 같은 명시적인 메모리 해제 방법을 통해 메모리 해제를 직접 제어한다.
+ Destroy에는 delay 시간을 넣을 수 있는데, delay 0을 하게 되면, 내부적으로 삭제 큐에 정보를 넣고, 다음 프레임에 삭제된다.
3. UnityEngine.Object의 메모리 관리 방식
Unity에서 UnityEngine.Object 객체는 C#의 가비지 컬렉터가 관리하지 않기 때문에, 객체의 생명 주기를 개발자가 직접 제어해야 한다. 예를 들어, Destroy()를 호출하여 객체를 명시적으로 제거해야 하며, C#의 GC가 이 객체의 메모리를 관리하지 않기 때문에 null을 할당하거나 참조를 끊는 것만으로는 메모리 해제가 이루어지지 않기에 메모리 누수가 발생할 수 있다.
Unity에서는 메모리 해제 시점에 대한 제어권을 엔진이 가지고 있으므로, Destroy() 또는 Resources.UnloadUnusedAssets() 등의 방법을 사용하여 객체를 해제할 수 있다.
4. 주요 차이점
특성 | C#의 object | Unity의 UnityEngine.Object |
메모리 관리 | GC에 의해 관리됨 | Unity 엔진의 메모리 관리 시스템에 의해 관리됨 |
GC 영향 | 영향 O | 영향 X |
명시적 메모리 해제 | 불필요 (자동 관리) | Destroy(), UnloadUnusedAssets() 등을 사용하여 명시적으로 관리 |
주요 사용처 | 일반적인 C# 객체들 | Unity 엔진 내에서 사용하는 오브젝트 (예: GameObject, MonoBehaviour 등) |
5. 결론
Unity에서 UnityEngine.Object는 C#의 기본 object와는 다른 방식으로 메모리가 관리되며, C++의 힙 매니저와 비슷하게 관리되기에 Destroy()와 같은 명시적 메모리 해제 방법을 사용해, 메모리 누수로 인한 성능 저하를 방지해야한다.