메모리 관리에 대한 탐구 (4) - 가비지 만들기와 리소스
C#에서는 가비지 컬렉터가 더이상 참조되지 않는, 즉 사용되지 않는 이른바 쓰레기 메모리, 즉 가비지들을 수거해간다.
가비지 컬렉터가 메모리를 자동으로 관리해주긴 하지만, 가비지 컬렉터는 참조가 하나라도 있으면 사용된다고 간주하기 때문에 더이상 사용하지 않는 것들의 참조를 해제함으로써 가비지 컬렉터가 수집해갈 수 있도록 해줄 필요가 있다.
즉, 가비지로 만들어주는 작업이 필요한 셈이다.
스크립트 상에서 사용하고 있는 변수에 null을 넣어서 참조를 없애주는 방법이 있지만, 다른 곳에서 참조하고 있을 수도 있기 때문에 제대로 사라질 것이라는 보장은 없다. 특히 오브젝트의 경우 null을 넣어도 제대로 해제가 되지 않으니 Destroy()를 사용하라고 하는데, 확인해보니 GameObject는 Transform과 상호 참조를 하기 때문에 쓰는 곳도 없는데 메모리에 남아있는 문제가 있었다.
그림으로 그려보면 대충 이런 느낌일 것 같다.
가비지 컬렉터가 쓰지 않는 메모리들을 수거해주긴 하지만, 우리가 참조를 끊음으로써 가비지 컬렉터가 수거해갈 수 있도록 만들어줘야 한다.
만약 이런식으로 사용하지 않는데 참조 해제를 제대로 해주지 않는 일이 반복되면 메모리 상에 쓰레기가 계속 쌓이고, 결국 메모리가 부족해지는 때가 올 것이다. 부족해지면 메모리를 늘리다가 한계에 다다르면 어플리케이션이 종료되는 마지막을 맞이하게 된다.
이렇게 되지 않기 위해서 우리는 가비지 컬렉터를 믿고 맡길 것이 아니라 메모리의 사용이 끝나면 제대로 해제해줘야 한다.
추가로, Garbage Collector는 Managed Heap만을 관리해준다. 여기는 프로그래머가 사용하는 각종 Class들이 주로 저장된다.
Texture, Mesh, Material 등 Resource들은 Native Memory에서 따로 관리를 해준다. Native Memory는 유니티 내부에서 C++로 관리를 하는 것으로 알고 있는데, GC가 관리하는 영역이 아니기 때문에 Load한 Texture, Mesh 등의 참조를 전부 끊고 GC를 돌려도 수집해가지 않는다. Native Memory에서 Resource들을 해제해주려면 Resources.UnloadUnusedAssets()를 호출해줘야 수거해간다. (씬이 생성될 때 가져온 리소스들은 삭제되지 않는다든가 하는 내용을 봤던 것 같은데, 어디서 봤는지 잘 모르겠다)
아무튼, Native Memory를 관리해주는 가비지 컬렉터가 따로 있는 셈이다. 그런데, Native Memory는 대체로 용량이 큰데다가 가비지 컬렉터와 같은 점진적 수집 기능이 없어서 그런지 Resources.UnloadUnusedAssets()을 호출하면 게임이 잠깐 뚝하고 끊긴다. 로딩을 할 때라든지, 메모리가 부족할 때(onLowMemory가 호출되었을 때 등) 호출하는 것이 바람직하다.
===========================================================================
뭔가 그림이 있으면 재밌을 것 같아서 그려봤는데, 나름대로 괜찮게 그려진 것 같다.
최근 회사에서 메모리 관리에 대해서 맡게 되어 메모리 프로파일러를 보는데, 처음에 이런 내용을 모르고 보니까 머리에 내용이 들어오지 않았다. 여러 방면으로 삽질하고 공부하다가 최근 이해가 되어서 나름대로 정리해본 내용이다. 다음엔 아마 메모리 프로파일러 보는 법에 대해서 적지 않을까 싶다. 익숙해지는데 시간이 좀 걸리고, 많이 복잡하지만 메모리 관리에 상당한 도움을 주는 좋은 도구이다.