[Unity] Foreach의 GC의 원인 및 수정
Unity 5.5 미만 버전에서 foreach를 사용할 경우 가비지가 발생하는 이슈가 있었다.
원인
- Mono C# Unity 버전에서 foreach loop 종료 시 값을 강제로 박싱
- 값 형식의 열거자를 생성하였는데, 해당 열거자를 사용할 경우 loop가 종료되는 시점에 IDisposable 인터페이스를 구현 해야 했음
- 인터페이스는 참조 형식이기 때문에, 값 형식을 인터페이스로 변환하는 중에 박싱이 발생하게 됨
참고 : https://everyday-devup.tistory.com/106
[Unity] Struct (구조체)
Struct (구조체)? - 데이터와 관련 기능을 캡슐화할 수 있는 값 형식 캡슐화 : 데이터와 기능을 하나로 묶고, 구현 내용 일부를 외부에 감춤 특징 1. 매개 변수가 없는 생성자를 선언할 수 없음 ( 단,
everyday-devup.tistory.com
대응
- Unity의 C# 컴파일러가 최신 버전의 Mono 컴파일러로 업그레이드 되면서 수정 됨
List<int> intList = new List<int>{ 1, 2, 3 };
foreach( int i in intList) {}
// foreach IL GetEnumerator
[__DynamicallyInvokable]
public List<T>.Enumerator GetEnumerator() => new List<T>.Enumerator(this);
// valueType의 eumerator는 IDisposable interface를 구현
public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator
public interface IDisposable
{
[__DynamicallyInvokable]
void Dispose();
}
// Unity 버전 5.5 미만에서는
finally
{
IL_0030: ldloc.2 // V_2
// valueType에서 interface (참조형식) 박싱을 해서 Dispose를 호출
IL_0031: box valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
IL_0036: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003b: endfinally
}
// Unity 버전 5.5 이상에서는
finally
{
IL_0035: ldloca.s V_0
// 박싱이 발생하지 않음, ValueType List<T>.Enumerator의 포인터를 직접 넘겨주게 변경 됨.
// C# constrained접두사는 callvirt this Type 이 값 형식 인지 또는 참조 형식 인지에
// 관계 없이 일관 된 방식으로 명령을 수행할 수 있도록 설계 되었습니다.
IL_0037: constrained. valuetype [netstandard]System.Collections.Generic.List`1/Enumerator<int32>
IL_003d: callvirt instance void [netstandard]System.IDisposable::Dispose()
IL_0042: endfinally
}
* 출처 및 참고 블로그
https://docs.unity3d.com/kr/2019.4/Manual/BestPracticeUnderstandingPerformanceInUnity4-1.html
관리되는 힙에 대한 이해 - Unity 매뉴얼
많은 Unity 개발자들이 겪는 또 다른 일반적인 문제는 예기치 않은 관리되는 힙(managed heap)의 확장입니다. Unity에서 관리되는 힙은 축소되는 것보다 더 쉽게 확장됩니다. 이와 더불어, Unity의 가비
docs.unity3d.com
(중급) 유니티의 Foreach는 여전히 악의 근원일까요?
대한민국 모임의 시작, 네이버 카페
cafe.naver.com
https://docs.microsoft.com/ko-kr/dotnet/api/system.reflection.emit.opcodes.constrained?view=net-6.0
OpCodes.Constrained 필드 (System.Reflection.Emit)
가상 메서드가 호출되는 형식을 제한합니다.Constrains the type on which a virtual method call is made.
docs.microsoft.com