힙 메모리 영역과 스택 메모리 영역의 차이에 대해 알아보자.

 

[1]

우선 스택이 왜 존재하는지부터 알아보자. 흔히 스택은 정적 메모리 할당(static memory allocation)을 위해 존재한다고 말한다. 맞는 말이지만 조금 더 직접적으로 표현하자면 스택은 함수의 콜 스택(call stack)을 위해 존재한다고 할 수 있다. 즉 스택 메모리는 함수의 호출/반환과 지역변수들을 위해 사용한다.

스택프레임 - from Wikipedia

source : Wikipedia

새로운 함수를 호출 했을때, 함수 내부에서 새로운 콜 스택을 만드는 과정을 x86 기준으로 보면, 다음과 같다. (이를 function prologue라고도 한다) 참고로 여기서 베이스 포인터 == 프레임 포인터이다.

push ebp 		// 이전 프레임의 base pointer를 스택에 저장해 놓는다.
mov ebp, esp 	// 새로운 base pointer = 이전 프레임의 stack pointer
sub esp, size	// 새로운 프레임의 필요 size만큼 stack pointer를 빼준다.

새로운 스택 프레임의 size(=지역 변수 + 리턴 주소 + 인자)는 컴파일 시점에 정적으로 결정되어 있다. 또한 그림에서 top으로 표시된 쪽이 주소 값이 작은 쪽이므로 size를 더해주는 것이 아니라 빼줌으로써 새로운 스택 프레임의 공간을 확보한다. 또한 이전 프레임의 베이스 포인터를 push 하는데, 이는 return시에 이전 프레임을 복원하기 위함이다. 이전 프레임의 스택 포인터는 저장하지 않아도 계산할 수 있다. 위 그림을 참고하자. 

 

리턴시에 콜 스택을 해제하는 과정은(마찬가지로, 이를 function epilogue라고도 한다)는 다음과 같다.

mov	esp, ebp	//스택 포인터 복원
pop	ebp		//베이스 포인터 복원
ret			//return address로 jump

 

함수 콜 스택은 이와 같이 간단한 명령어들만으로 생성/소멸할 수 있다. 함수 입장에서는, 자신만의 메모리 영역이 온전하게 할당되어 있고, 함수가 리턴될 때 까지 유지되며, 리턴 시에 해제되는 메모리 영역을 가지고 있는 것이다. 이러한 구현은 위의 간단한 명령어들만 으로 추상화되어 이루어진다. 실로 우아한 구현이 아닐 수 없다.

 

[2]

하지만 힙 메모리 할당/해제는 이보다 훨씬 더 복잡하다. 스택 메모리의 할당/해제는 명령어 아키텍처 별로 정의되어 있고, 그나마도 아키텍처마다 대동소이한 정도의 차이를 보인다. 하지만 힙 메모리 할당/해제는 운영체제가 관리하게 되며, 운영체제의 종류마다 다르고, 운영체제의 버전 마다도 상당한 차이를 보인다. 여기서는 구체적인 구현 대신에, 왜 이렇게 복잡하고 운영체제마다 구현 방식이 나뉠 수밖에 없는지 고찰해보자.

 

우선 힙 메모리 영역은 동적으로 메모리 할당을 하기 위한 영역이다. 이 말은, 프로그램 흐름의 어느 곳에서, 얼마만큼의 메모리를 할당/해제해야 할 지 정적으로(=컴파일 타임에) 알 수 없고 런 타임에 결정해야 한다는 뜻이다. 이 때문에 연속적인 메모리를 관리하는데 많은 어려움이 존재한다. 이런 문제점들을 어떻게 우회하는지에 대한 철학이 운영체제마다 다르기 때문에 힙 메모리 할당/해제의 구현이 다른 원인이 된다. 아래에서 대표적인 어려움 하나를 소개한다.

 

예를 들어 아래와 같은 코드를 생각해보자.

ptr1 = malloc(5)
ptr2 = malloc(10)
ptr3 = malloc(5)

free(ptr1)
free(ptr3)

ptr4 = malloc(10)

메모리 0번지에서 차근차근 할당을 한다고 하면, ptr1은 0~4 / ptr2는 5~14 / ptr3는 15~19가 할당되었을 거고, 이 중 ptr1과 ptr3는 해제되었다. 여기서 10바이트의 ptr4를 할당할때, ptr1과 ptr3가 연속적이지 않으므로(=파편화되어있으므로) 할당할 수가 없고, 어쩔 수 없이 새롭게 20~29번지를 할당해야 한다. 이를 메모리 파편화라고 한다. 이를 어떻게 해결, 혹은 우회할 것인지에 대한 구현이 운영체제마다 다르다. 여기까지만 봐도 당연히 [1]의 스택 메모리 할당/해제보다 복잡할 수 밖에 없다.

 

[3]

[1],[2]를 통해 자연스럽게 스택/힙 메모리의 차이점을 알 수 있다. 간단히 정리해보자.

  스택
할당/해제 빠름 느림
구현 난이도 쉬움 - 아키텍처별 대동소이 어려움 - 운영체제마다 다름
필요성 함수 프레임 제공 메모리 동적 할당
scope local global
유연성 resizing 불가능 resizing 가능

 

 

 

Reference

1. https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap

 

+ Recent posts