유니티엔진의 메모리 관리방식
이전에 가비지컬렉터(유니티)관련 포스팅을 하면서 유니티(C#, Monobehaivor)는 참조 카운팅 방식으로 메모리를 관리한다고 한적이 있다. 정리하자면 할당된 메모리를 참조하고 있는 수가 0이되면 사용하지 않는 메모리(가비지)로 판단하여 GC가 자동으로 회수한다.
유니티를 시작한지 얼마 안되었을때다. 사용이 끝난 오브젝트들을 Destory()함수로 파괴해주고, 참조하고 있던 캐시를 null로 바꿔주어서 참조카운트를 0으로 바꿔주는 식으로 사용한적이 있다. 하이라키상에서도 게임씬상에서도 사라진상황이고, 프로파일러 상에서도 참조카운트는 0이었다. 그런데도 왠걸, 메모리가 계속해서 증가하는게 아닌가? 메모리 누수였다.
로드된 리소스의 메모리 누수
분명 하이라키상에서도 없고, 씬에서도 없고, 참조개수도 0인데… 가비지인데. GC가 회수를 안해간것일까? GC가 고장난것인가? 무슨 상황이지? 하고 당황했던 기억이 있다.
이유는 오브젝트가 ‘로드된 리소스’ 였다는 것이다. Instantiate() 함수로 인스턴스화한 오브젝트는 일반적으로 Destory()를 통해서 가비지 수집 대상이 된다. 하지만 인스턴스화한 재료가(원본, original) public 변수로 참조된 오브젝트(프리팹 등)이거나 Resource.Load()로 로드된 리소스(텍스쳐, 프리팹 등)라면? 씬상에 없어도, 참조카운트가 0이어도 Mono의 GC는 해당 메모리를 회수 하지않는다…
관리하는 영역이 다르기 때문이라고 한다. 인생…
해결방법은?
이경우 Resources.UnloadUnusedAssets() 함수를 호출하면 된다. 유니티 문서에는 다음과 같이 설명되어 있다. “씬에서 사용되지 않고있는 에셋에 할당된 메모리를 해제 합니다.” 즉, 씬상에서 존재하지 않는다면, 위에서 설명한대로 하이라키상에 없고 참조카운트가 0이라면 해제를 해준다는 뜻이다. 수동으로 관리하는 리소스들에 관해서는 메모리 누수에 조금더 신경쓸 필요가 있는거 같다.