유니티에서 Vector를 new 연산자로 호출하지만 가비지에 영향을 주지않는다. 왜 그렇게 될지에 대해 생각을 하게되었다.
메모리 타입
객체는 생성 시 메모리 구조 중 Stack과 Heap에 저장된다.
Stack
객체가 값 형식으로 메모리에 할당되며, 해당 형식에서는 할당된 동일한 메모리 위치의 변수의 값이 변경된다.
또한 함수 호출이 될 때마다 변수는 Stack에 할당 된 값을 가져온다. 함수 호출이 끝날 때 마다 변수에 대해 할당된 메모리는 해제가 되며 함수 속 선언된 변수들도 모두 해제된다.
Heap
객체가 참조 형식으로 메모리에 할당되며, 할당된 객체의 메모리 주소를 참조를 한다.
그렇기에 변수가 전역으로 접근이 가능하다.
Heap은 동적으로 할당되며 끝이나면 메모리를 해제를 해야된다. 메모리를 해제하기위해 사용되는 것이 가비지컬렉터(GC)이며 자동적으로 쓰지않는 메모리를 해제한다.
Struct와 Class
C#에서 Struct는 값 형식으로 Stack 메모리에 할당된다.
반면 Class는 참조 형식으로 Heap 메모리에 할당된다.
그렇기에 Struct를 사용하였을 때 GC가 발생되지 않도록 할 수 있다.
Struct를 쓴다고 Stack에 모두 저장되지는 않는다.
private void SetVertex()
{
Vector3 segmentPos = startTr.position;
for (int i = 0; i < segmentCount; i++)
{
segments.Add( new Segment(segmentPos));
/// y of segment's position goes down by segmentLength.
segmentPos.y -= segmentLength;
}
}
중간언어인 IL로 컴파일 된 것을 보면, newobj명령어가 실행되는 것을 볼 수 있다.
newobj 명령어는 힙에 저장하여 인스턴스를 생성한다는 것으로 값들이 힙에 저장됨을 볼 수 있다.
private void SetVertex()
{
Vector3 segmentPos = startTr.position;
for (int i = 0; i < segmentCount; i++)
{
Segment currentSegment = new Segment(segmentPos);
segments.Add(currentSegment);
/// y of segment's position goes down by segmentLength.
segmentPos.y -= segmentLength;
}
}
이를 해결하기위해 값을 복사할 변수를 선언하여 저장하는 List에 추가하는 방식으로 전환하였다.
IL을 확인하여보면 지역변수에 저장하기위해 call이 실행되고 list에 저장하기위해 callvirt가 실행되어 힙의 생성 없이 저장되는 것을 볼 수 있다.
댓글