본문 바로가기
----- IT -----/Unity3D

유니티3D 메모리 관리(Unity3D Memory)

by 대소니 2014. 1. 18.

유니티3D 메모리 관리(Unity3D Memory Management)

C#의 메모리는 C와 다르며, 키워드는 stack과 heap이다
stack은 순서가 있고, 빠르며 다만 제한적이고, 
heap은 랜덤이고, 사이즈가 크며 단 느리다.


▣ value Types and the Stack

A stack is a pile of variables, just like a pile of plates, you can only remove the top one or add on the top but you cannot remove the bottom one or add at the bottom.


The stack is used for function calls
재귀함수를 사용한 무한루프에 빠졌을때 stack overflow를 보게 될 것이다



n = 30; number = 10;
인수는 copy되고 로컬 변수는 함수{}가 끝나면 소멸한다
update 함수에서는 변수 n, inside가 보이지 않으며 접근하려고 할때 컴파일 에러가 발생한다.




▣ Lifetime and scope

lifetime means the time in the program the variable exists, scope defines the zones of the program the variable is visible

stack에 저장되는 변수 타입들
  • int
  • unsigned
  • float
  • double
  • char
  • struct
  • bool
  • byte
  • enum
  • long
  • short

이런 automatic variable은 컴파일러가 제어한다. scope이 끝나면 수명을 다하며, 프로그래머가 메모리 해제를 신경쓰지 않아도 자동적으로 된다.



▣ Reference Types and the Heap

A reference type variable is an object stored in memory as a reference
새로운 class의 instance가 생성될 때 data와 data의 주소는 heap에 저장이 된다. new 를 사용하여 instance를 생성하면, object타입과 instruction(오브젝트를 초기화)에 상응하는 메모리의 공간을 얻고, 







enemyPrefab에 접근이 불사하여 reference가 필요함

enemyRef는 if문의 scope에서만 사용이 가능하며 update에서는 사용이 불가능하여 다음과 같이 사용함

GameObject enemyRefAgain = GameObject.Find(“Enemy”);

heap에 저장되는 reference type variable들
  • class
  • object
  • string (even though string looks like a variable, it is an actual object instantiation of the classString)



결과는? (동일한 값)




▣ Lifetime, Scope and Garbage Collection

c, c++에서는 프로그래머가 메모리를 해제해주었다(free())
c#에서는 garbage collector가 해당 메모리의 사용 여부를 체크한다.
reference counting system은 두가지 경우(부모가 참조하거나 자식 객체가 참조할 경우)를 체크하여 이는 메모리를 free 시키지 않는 원인이 된다. 하지만 GC는 이런 제한이 없다.

Unity에서의 GC는 collection process를 수행하는것에 있어서 일반적인 GC와는 조금 다르다.
전형적인 C#에서의 GC는 작은 비용의 메모리와 작고 짧은 생명의 ojbect들을 해제하는 것으로 이해하지만 Unity에서는 그렇치 않다.

매순간 메모리 block이 시스템을 채우는지 모든 heap에 존재하는 object들을 체크해야 한다.
이것은 code를 access하고 running하기 위한 체크이며 특정 object를 유지할 수 있으면 유지하
나 그렇치 않을 경우에는 release를 해야한다.

보통의 .NET GC는 generations라고 불리우는 것들이 일어난다. 이는 최소한의 운영이 가능한 메모리 사이즈를 찾는 것이며, 새롭게 생성되는 object들을 먼저 메모리에 올려보고 유지가 불가능하다고 판단되면 오래된 object들을 해제하는 방식이다.

Unity의 .NET GC와는 조금 다르게 모든 object들을 항상 테스트하기에 major overhead가 된다. 갈길이 좀 멀긴하다. 

Reachability testing은 Garbage Collection의 마술과 같다.
이것은 어떤 코드가 현재 running 상태 인지 아니면 running이 가능한 상태인지를 결정하기 위한 collector를 가능하게 한다. 이는 코드들은 모두 하나의 reference 참조를 가지고 있고, collection의 대상이 되는 object의 reference 참조를 가지고 있기 때문이다. 함수내에 closure를 사용하는 것도 마찬가지이다. 

(A closure is created by referring to some local variable or parameter inside an anonymous function defined inside a function. it's very complicated and very powerful, but it isn't fast in the way games need things to be fast.)





GC는 가용한 메모리가 충분치 않을때 발생합니다. 외부 접속이나 외부 리소스를 사용할 경우에는 명시적으로 close를 해야 합니다. 파일을 열거나 DB를 접속시에 연결 해제에 신경을 써야 합니다. (특히 fail시)

GC는 expensive operation이며, 매뉴얼로 트리거 할 수 있다. 게임 플레이에 지장이 없는 레벨을 로드할 때나 pause 할 때가 좋다. System.GC.Collect();로 GC를 trigger할수 있다

GameObject.Find() 를 사용하여 any scripts의 오브젝트 참조를 가져올 수 있다.





▣ Struct vs Class


a structure is a value type when a class is a reference type




So, classes save memory but structures are faster
class는 heap에 저장되며 GC에 의해 해제가 된다. structures는 stack에 저장되며 GC와 무관하다

unity에서 사용하는 structures은 다음과 같다
position, color, quaternion, rotation, scale…

짧은 life의 자주 alloc되는 object는 structs가 유리하며, 긴 life의 큰 object는 class를 사용하는 것이 유리하다. Vector3, RaycastHit등의 작은 많은 object들이 유니티에서 사용되는 structs들이다




▣ Creating a Reference

boxing 은 value type을 reference type으로 변환한다
int a = 100; object b = a;
unboxing은 reference type을 value type으로 변환한다
int a = 100; object b = a; int c = (int)b;







Static

A static variable in .NET is stored in a special part of the heap called the High Frequency Heap.

▣ static classes

스태틱 클래스는 인스턴스화할 수 없으며, 생성도 할 수 없다.
때문에, 단 하나의 클래스로서의 저장공간만을 가지고 있다.
사용자가 메모리를 할당하고 생성하는 것이 아니라 프로그램이 heap에 메모리 공간을 할당하고 생성한다.(처음 접근하거나 호출될 때 생성되고 프로그램이 종료할때 해제된다. 사용하지 않으면 절대 생성되지 않는다.)



This can be done anywhere and anytime in the game. A static class or member has scope in every file and lifetime of the whole program.

새로운 scene이 로딩이 되면 기존 scene은 사라진다. 이때 점수와 같이 전달하고자 하는 데이터가 있을 경우 유용하게 사용된다
그리고, 게임을 떠날때 static class의 데이터를 save한다








▣ static variables





enemy가 하나일때는 이상이 없으나 다수의 enemy가 존재할때 하나만 죽어도 모두가 죽게 된다. 왜냐하면, health를 공유하기 때문.



다음 scene으로 넘어가기 위한 체크를 아래와 같이 할 경우


reference value를 사용하거나 오류가 발생하여 적을 다 죽였음에도 counter가 1이면 끝나지 않는 버그가 된다
적 카운트, 플레이어의 hp는 static으로 하는것이 좋다
혹은 인스턴스의 맴버를 접근하고자 할때 해당 데이터가 없을 경우가 발생할 수있다




▣ static functions

스태틱 함수는 non 스태틱 클래스 내에 구현이 가능하고 이 함수가 사용할 수 있는 맴버 변수는 static만 가능하다

스태틱 함수는 non 스태틱 함수보다 빠른 경향이 있다. 인스턴스 함수를 호출할때 컴파일러가 존재 여부를 체크하며,  스태틱 함수는 항상 존재함이 보장된다. 작은 차이이다.

스태틱 함수의 다른 유용성은 영구적으로 존재하며 확장성이 좋다











▣ Heap Fragmentation, Garbage Collection and Object Pooling

heap은 큰 저장소이며 데이터가 랜덤으로 저장된다. 완전 랜덤은 아니고 os에 의해 데이터가 저장되기 충분한 공간을 찾아서 저장하게 된다. heap의 사이즈나 위치는 사용자의 platform에 의존적이다





c 프로그램에서는 실제 공간이 있음에도 데이터를 할당할 연속적인 공간이 없으면 할당을 하지 못한다. unity는 .NET의 메모리 관리를 사용하여 가용한 연속적인 공간이 없으면 fragmentation을 제거하거나 데이터를 이동시킨다. GC가 있어서 이런일이 거의 발생하지는 않는다

Object pooling을 사용하면 heap fragmentation을 피할 수 있다. destroy대신에 deactivate한다. 이는 메모리 문제를 해결하고, instantiate와 destroy 함수를 사용하지 않아도 되는 이점이 있다
gameObject.SetActiveRecursevely(true/false);



적 웨이브가 올때 enemy를 죽여서 destroy하는 대신에 reposition을 하여 다음 웨이브로 돌리는 방식으로 사용하면 같은 적을 여러차례 죽이는데 사용할 수 있다

총알도 player나 여러 npc가 쏘는 총알들을 disable하고 총 앞이나 적절한 위치에 다시 위치시켜서 enable한다

pooling 메모리는 os에 따라 다른 프로세스로 관리되어 object 사이즈에 무관하게 항상 같은 메모리 사이즈를 유지하도록 구성하자




▣ Array Reuse

array or List는 update() 함수와 같이 자주 실행되는 함수내에서 생성하는 것은 GC를 발생하게 하는 주요 원인이다








댓글0