본문 바로가기

Hub Development/Computer Science

[CS] 프로세스 메모리 구조

728x90

📌  프로세스의 메모리 구조


🔹 64비트 운영체제는 메모리 한 칸의 주소를 64비트로 표현하며 이는 8바이트와 같은 의미이고, 메모리 주소를 8바이트로 표현하기 때문에 포인터(주소를 가리키는 변수)의 크기 또한 8바이트이다.

 

메모리 구조는 통상 Text, Data, Heap, Stack 으로 구분된다.

위 이미지처럼 상수, 함수는 Text 영역에, 전역, 정적변수는 Data 영역에, 지역변수들은 Stack 영역에, 동적할당이 되는 변수들은 Heap영역에 위치하게 된다. (malloc 함수는 런타임(실행중)에 메모리를 동적으로 할당할 수 있는 함수.)

위 4개의 영역 중 Text영역이 가장 낮은 주소(0에 가까운 주소), Data영역이 그 다음 주소, Heap영역이 Data영역의 다음 주소, Stack영역은 4개 영역 중 가장 높은 주소에 위치한다.

 

먼저 상수는 Text영역이고 보면 다른 변수들과 비교했을 때 가장 낮은주소에 위치하는 것을 볼 수 있다.

 

함수는 상수와 마찬가지로 Text영역이고, Data 영역보다 낮은 주소에 위치한다. 상수와 거의 유사한 주소에 위치하는 것을 볼 수 있고 함수안에 있는 변수들은 먼저 Text영역에 있는 함수를 호출함과 동시에 내부에 있는 변수들은 Stack영역에 할당하게 된다. 그리고 해당 함수가 종료되면 Stack메모리에 있던 함수의 변수들은 모두 pop 된다.

 

전역변수는 초기화된 변수와 초기화되지 않은 변수 두 가지의 경우가 있다. 위 주소를 보면 상수보다는 높은 주소에, Heap영역인 동적할당변수보다는 낮은주소에 위치한다. (참고로 먼저 초기화 된 변수들이 더 낮은주소에 위치하고 모두 메모리에 올려지면 그 다음으로 초기화가 안된 변수들이 할당된다.)

 

정적변수 또한 전역변수와 마찬가지로 Data영역으로 상수 영역에 있는 데이터보단 높은 주소에, Heap영역에 있는 데이터 주소보단 낮은 주소에 위치한다.

 

동적할당 변수의 경우 Heap영역이다. Text, Data보다는 비교적 높은 주소에서 시작하지만, Stack영역에 비해서는 한없이 작은 주소에서 시작하는 것을 볼 수 있다.

 

마지막으로 지역변수의 경우 Stack영역이다. Stack영역은 다른 영역과는 다르게 높은주소에서 낮은 주소로 메모리에 할당된다. 즉, 어느정도 높은 주소부터 시작하여 지역변수들이 선언될 때마다 낮은주소로 쌓인다. 이를 확인하기 위해 2개의 지역변수를 선언했고, 지역변수1은 0x7ffeefbff47c 이고, 그 다음으로 선언된 지역변수2는 int형 변수이므로 4byte의 크기이니 4칸만큼 작은 주소인 0x7ffeefbff478에 위치하는 것을 볼 수 있다.

 

📑 Buffer Overflow


🔹 Buffer Overflow는 버퍼(Buffer)가 할당된 메모리를 넘어가는 상태를 말한다. 여기서 버퍼(Buffer)란 보통 메모리를 가리키며, 메모리의 한 부분을 말한다. 이러한 오버플로우는 주로 Stack과 Heap 영역에서 발생한다.

 

Stack과 Heap은 프로그램이 실행될 때 동적으로 할당되는 데이터를 저장하는 메모리 공간이다. 이들 영역의 버퍼를 의도적으로 넘치게 만들어 인접한 데이터 영역을 침범하고, 때로는 프로그램을 비정상적으로 종료시키거나 시스템 권한을 획득할 수 있다.

 

또한, 메모리에 할당된 변수의 크기보다 큰 데이터를 입력하는 경우도 오버플로우라고 한다. 즉, 오버플로우는 여러 형태로 나타날 수 있으며, 주로 Heap Overflow와 Stack Overflow로 나뉜다.

 

🤔 Stack Overflow


🔹 Stack Overflow는 호출 스택이 할당된 스택 영역의 한계를 초과할 때 발생한다. 이는 주로 '재귀 호출'에서 발생하는데, 재귀를 무한히 호출하거나 탈출 조건이 없는 경우에 해당한다. 이런 경우 호출 스택이 계속해서 쌓이다가 한계를 넘어가면 Stack Overflow가 발생한다.

Stack Overflow를 방지하기 위해서는 재귀 호출을 피하는 것이 좋고, 사용해야 한다면 반복문을 사용하거나 재귀 호출을 사용할 때 탈출 조건을 명확히 정의하여야 한다. 이렇게 하면 호출 스택이 너무 많이 쌓이지 않아 Stack Overflow를 방지할 수 있다.

 

🍪  Heap Overflow


🔹 Heap Overflow는 힙 영역에서 할당된 메모리 영역을 넘어설 때 발생한다. 이는 주로 매우 큰 데이터를 생성하려고 할 때 OutOfMemory와 같이 발생한다. 스택과 마찬가지로 Heap 영역에서도 일정한 크기를 초과하는 데이터가 들어올 경우 발생하는데, 스택에서는 주로 지역 변수들이 관리되는 반면, 힙 영역에서는 동적으로 관리되는 데이터들이 발생하는 것이다.

 

언어에 따라 Heap에서 관리하는 데이터의 형태는 다르지만, 주로 동적 할당 함수인 malloc()과 같은 함수, 객체, 참조 변수들이 힙 영역에서 관리된다. 예를 들어, C 언어에서는 new 연산자가 없으며 배열과 같은 동적 할당 변수는 malloc() 함수를 사용하여 할당된다. 반면 C++나 Java와 같은 언어에서는 new 연산자를 통해 객체를 힙 영역에 할당할 수 있다.

int arr[10];		// C, C++ : Stack 영역
int *arr = new int[10]	// C++ : Heap 영역
int[] arr = new int[10]	// Java : Heap 영역

 

Java와 같은 언어에서는 자바 가상 머신(JVM)이 가비지 컬렉터(Garbage Collector)를 통해 더 이상 필요하지 않은 데이터를 자동으로 해제해주므로 힙 메모리 관리가 상대적으로 용이하다. 하지만 C나 C++과 같은 언어에서는 가비지 컬렉터가 없기 때문에 프로그래머가 수동으로 할당된 메모리를 해제해야 한다.

 

또한, 더 이상 필요하지 않은 동적 할당 변수를 해제하지 않는 경우 메모리 누수(Memory Leak)가 발생할 수 있으며, 힙 메모리 부족 현상을 피하기 위해 동적으로 관리해야 하는 변수들의 크기를 적절히 예측하고 제한해야 한다.

 

📸 참조


https://st-lab.tistory.com/198