개발/공부

메모리 관리에 대한 탐구 (1)

메피카타츠 2022. 12. 29. 12:41

CS 면접 관련 질문들을 공부하면서 스마트 포인터에 대해 찾아보게 되었다. 이와 관련해서 안에 쓰여져 있는 내용들 중 잘 모르는 것이나 궁금한 것들을 찾아 꼬리에 꼬리를 물다보니 꽤나 오랜 시간 동안 찾아보게 되었다. 그 과정에서 알게 된 내용이나 생각한 내용을 정리해보고자 한다.

 

출처 : 코딩의 시작, TCP School

메모리 관리에 대해서 가장 먼저 알아야 할 내용은 메모리 구조이다.

메모리 구조는 위에 보이는 것처럼 되어있는데 이 중 프로그래머가 신경써야 할 부분은 힙 영역이다.

나머지 영역은 프로그램이 알아서 관리를 하지만, 힙 영역은 프로그래머가 동적으로 메모리를 할당할 수 있는 부분이기에 실수가 발생할 수 있기 때문이다.

 

예를 들면 이런 것이다. 게임을 하면서 던전에 들어갈 때 동적인 메모리를 할당받아 10MB만큼 사용했다고 치자. 그런데 던전이 끝날 때 할당받은 메모리를 반납하지 않으면 어떻게 될까? 이 메모리 공간이 다시 사용될 일이 없다고 할지라도 프로그램을 이를 사용 중인 공간으로 인식한다. 이것이 메모리 누수이다. 램 누수라고도 한다.

새로운 메모리를 할당받고 반납하지 않는 일을 반복하게 되면, 사용하지도 않으면서 공간을 차지하는, 소위 쓰레기 메모리들이 늘어나게 된다. 그러면서 프로그램은 점차 느려지게 된다.

힙 영역에 쓰레기 메모리들이 쌓이면서 힙 영역이 부족해지면 프로그램은 힙 영역을 확장한다. 이를 반복하다보면 메모리에 한계가 오게 되고, 결국 더 이상 확장할 수 없게 되면 프로그램을 강제 종료한다. 우리가 램누수 게임이라고 부르는 게임들이 자주 튕기는 이유이다.

 

다행스럽게도, 유니티에서 사용하는 언어인 C#에서는 가비지 컬렉션이라는 기능을 제공한다. 접근할 수 없는 객체들을 주기적으로 정리하여 메모리 누수를 방지해준다. 단, 가비지 컬렉션은 코스트가 높은 작업이기 때문에 자주 실행되면 프로그램의 성능이 저하될 수 있으므로 쓰레기는 최대한 적게 만드는 것이 좋다.

 

C와 C++의 경우는 가비지 컬렉션 기능이 없다. 아마 포인터를 사용하기 때문인 것 같다. 가비지 컬렉션을 수행하면서 메모리의 주소를 자주 바꾸기 때문에, 포인터를 사용하는 C나 C++과는 상성이 좋지 않은 것 같다. 실제로 C#에서는 포인터를 사용할 수 있지만 사용하려면 안전하지 않다는 의미의 unsafe를 동반하는 코드를 작성해야 하며, 변수의 주소를 고정하는 fixed 기능을 제공하거나, 일반적인 포인터 사용을 대체하는 ref, out, in을 제공하는 것을 봐서는 어느정도 맞는 추측일 것 같다.

 

때문에 C의 경우, malloc() 혹은 calloc()으로 할당받은 메모리를 free()로 해제해주어야 하고, C++의 경우 new로 할당받은 메모리를 delete()로 해제해주어야 한다. 다만 C++은 다행히도 가비지 컬렉터처럼 메모리 누수를 방지해주는 스마트 포인터라는 개념이 도입된 모양이다. 가비지 컬렉터와 비교하면 사용은 조금 불편한 것 같지만 효율은 좋은 것 같다.

 

추가로, 언리얼은 C++ 기반인데, 그러면 가비지 컬렉터가 없나? 싶어 찾아보니 오브젝트의 베이직 클래스인 UObject에는 가비지 컬렉터를 사용하고, 코드 내에서는 스마트 포인터도 사용한다는 것 같다.

 

왜 이런 메모리 누수가 발생하는 것일까? 예전에는 개발 능력이 부족해서 그런걸까? 싶었지만, 지금은 생각이 바뀌었다. 아마 빠듯한 개발 일정 때문일 것이다. 이것저것 바쁘게 기능을 구현하다보면 코드를 되돌아볼 시간 따위는 없다. 구현하고 나서 "이렇게 하면 더 좋을 것 같은데" 싶은 생각이 들어도 남은 일을 쳐내기 바쁘다. 다 그런 것은 아니겠지만 나는 그랬다.

회사 입장에서도 하루라도 빨리 개발 일정을 앞당겨서 빠르게 자금을 회수하길 바랄 것이다. 특히 게임 업계는 망하면 손해가 이만저만이 아닌데, 고작 성능 개선을 위해 붙잡고 끙끙 싸매기보다는 빠르게 개발해주길 바라지 않을까? 그렇게 생각하면 램누수로 인한 팅김이 잦은 게임을 하며 짜증보다는 안타까운 마음이 앞선다. 그들은 아마 주어진 시간 내에서 최선을 다했을 것이다. 그래도 게임이 궤도에 오르면 보통 최적화 패치를 해주니 다행이다. 지금 생각해보면 어쩔 수 없는 수순인 것 같기도 하다.