프로그램이 실행되면 운영체제는 프로세스에 필요한 자원들을 할당해 준다. 그중 가장 중요한 자원 중 하나가 메모리이다. 프로세스는 할당된 메모리를 효율적으로 사용하기 위해 일정한 구조로 메모리 공간을 나누어 관리하는데, 이를 프로세스 주소 공간이라고 한다. 주소 공간은 각 영역별로 서로 다른 역할을 가지고 있으며, 프로세스가 어떻게 자원을 활용하고 관리하는지를 이해하는 핵심 요소이다.
이 글에서는 프로세스 주소 공간의 구성 요소인 Stack, Heap, Data, Text(Code) 영역을 하나씩 살펴보고, 이들 간의 상호작용이 프로세스와 스레드의 동작에 어떤 영향을 미치는지 알아볼 것이다. 이를 통해 메모리 관리의 효율성과 효율적인 메모리 사용을 위한 운영체제의 다양한 방법론에 대한 이해를 높이고자 한다.
프로세스는 운영체제가 자원을 할당하는 단위이다.
프로세스가 메모리를 할당 받으면, 자신만의 방법으로 메모리를 관리하기 위해 이 공간들을 어떠한 구조로 관리하는데, 이를 프로세스 주소 공간이라고 부른다.
메모리는 한정되어 있기 때문에, 프로세스는 다양한 방법으로 메모리를 절약하려고 시도한다.

🔖 Data 영역과 Stack 영역을 구분하는 이유
Heap과 Code 영역은 완전히 다른 역할을 하는데, Data와 Stack 영역을 굳이 구분하는 이유는 뭘까?
가장 큰 이유는 역할 분배이다.
Stack 영역을 통해 함수의 흐름을 관리하고, Data 영역(+BSS 영역)을 통해 전역 변수, static 변수를 관리한다. 만약 한 프로세스가 여러 개의 스레드를 갖는다면, 각각의 스레드는 자신만의 Stack 영역을 갖는다. 이는 스레드 내에서 수행되는 함수의 흐름을 각각 관리하기 위함이다.
또 다른 중요한 이유는 Data 영역의 공유를 위해서이다.
각각의 스레드는 Stack 영역을 갖고 Data 영역은 공유한다. 즉, Data 영역의 동일한 내용을 공유함으로써 각각의 스레드는 똑같은 공간을 여러 개 만들지 않고 메모리를 절약할 수 있다. (이는 Code 영역에서도 마찬가지다.)
🔖 Heap, Stack 크기에 대한 고찰
Heap과 Stack은 같은 메모리 공간을 공유한다.
같은 메모리 공간이지만 Heap 영역은 낮은 메모리 주소부터 할당받고, Stack 영역은 높은 메모리 주소부터 할당받는다. 즉, 같은 물리적 공간에서 서로 다른 방향으로 확장된다.
Heap의 크기는
-Xmx4096m와 같이 설정할 수 있다. 이 값은 프로그램이 사용할 수 있는 최대 Heap 메모리 크기를 지정하는 것이다. 이 크기는 프로그램 실행 중에 점점 커질 수 있으며, 필요에 따라 메모리를 동적으로 할당받는다.반면 Stack의 크기는 고정되어 있다. Stack의 크기는 메모리가 부족할 때 자동으로 늘어나지 않으며, 초과하면 “Stack Overflow” 오류가 발생한다. UNIX 계열에서
ulimit -s명령어를 통해 크기를 확인할 수 있으며, 기본적으로 8MB 정도로 설정되어 있는 경우가 많다.Stack 영역은 생성과 동시에 크기가 정해진다. 즉, 크기가 한 번 정해지면 바뀌지 않기 때문에, Heap 영역과 상관 없이 크기의 제한을 갖는다. 즉, 우리가 자주 볼 수 있는 Stack Overflow 같은 문제는, Heap 영역을 침범해서가 아니라 정해진 Stack 영역의 크기를 초과해서 발생한 문제라고 볼 수 있다.
🔖 스레드가 여러 개 있다면?
프로세스가 자원을 할당 받지만, 스레드도 자신만의 자원을 갖고 있어야 한다. 따라서 스레드도 자신만의 주소 공간을 갖고 있으나 약간의 차이가 있다.
다음 그림과 같이 실제로 각 스레드가 갖고 있는 것은 Stack 영역 밖에 없다.
즉, 나머지 공간은 프로세스의 값을 함께 쓰고 있고, 다른 스레드와 공유한다고 볼 수 있다.이때문에 Data 영역에 있는 자원은 동시에 여러 스레드가 접근할 수 있고, 동기화 문제가 발생할 수 있다.
이번 글을 작성하며 프로세스의 메모리 구조를 구체적으로 이해할 수 있게 되었다. 코드를 작성하면서 가끔 겪었던 스택 오버플로우가 왜 발생하는지, 선언한 변수와 동적 할당된 데이터들이 각각 어디에 저장되는지 알게 되었다. 특히 Stack과 Heap이 같은 메모리 공간을 서로 다른 방향으로 확장하는 점은 처음 알게 되어 더욱 의미있었다.
이번 학습을 통해 프로그램을 작성할 때 메모리 사용에 좀 더 신경 쓰며, 복잡한 작업을 처리할 때 메모리 관리 방식을 최적화하는 방법에 대해 꾸준히 고민할 필요가 있음을 느꼈다.
Reference