Stack과 heap
스택(stack)과 힙(heap)은 프로그램이 실행될 때 사용하는 두 가지 주요 메모리 영역이다. 스택은 함수 호추로가 로컬 변수를 저장하는 작고 고정된 영역이고, 힙은 동적 메모리 할당을위한 넓고 유연한 영역이다.
프로그램이 실행되면 운영체제는 프로그램에 필요한 메모리를 할당한다. 이 과정에서 먼저 프로그램의 코드와 전역 변수가 메모리에 적재된다. 프로그램 코드는 text segment로 불리며, 전역 변수와 static 변수는 data segment로 불린다. 그리고 stack과 heap이 각각 할당된다. 스택은 일반적으로 고정된 크기를 가진다. 반면, 힙은 처음에는 작게 설정되지만 필요에 따라 점진적으로 커질 수 있다.
스택(stack)
스택은 자료구조에서의 스택(LIFO Last In First Out)과 같은 구조를 가진다. 즉, 데이터를 넣을 때는 차례대로 쌓지만 제거할 때는 마지막에 넣은 것부터 꺼낸다. 이 구조 덕분에 O(1)의 매우 빠른 접근 속도를 가지며, 변수 할당과 해제가 매우 효율적이다. main을 포함한 모든 함수는 함수가 호출될 때마다 그에 필요한 매개변수, 지역 변수 및 함수 정보를 스택에 저장한다. 함수가 종료되면 해당 스택 프레임이 사라지면서 함수 내의 모든 변수도 함께 해제된다.
중괄호{}로 둘러쌓인 코드 블록 안의 변수도 스택에 저장된다. if 문, while문, for문 등도 마찬가지이다. 함수 내부에서 또다른 블록을 열거나 함수를 중첩해서 호출하더라도 문제가 발생하지 않는 이유는 각각의 블록이 독립적인 스택 프레임을 가지기 때문이다.
또한, 지역 변수가 지역 밖에서 사용될 수 없는 이유도 여기에 있다. 스택에 저장된 변수는 해당 블록이 종료될 때 자동으로 메모리에서 해제되기 때문에 블록 밖에서 접근할 수 없다.
JAVA에서는 원시 변수와 객체 레퍼런스가 stack에 저장된다. (최적화를 위해서 escape analysis를 통해 객체가 stack에 저장되는 경우도 있다.)
힙(Heap)
반면, 힙은 동적 메모리 할당을 위한 공간으로, 메모리 할당과 해제가 명시적으로 필요하다. 힙에 할당된 변수는 프로그램이 직접 관리해야 하며, 이 과정에서 메모리 누수(memory leak)가 발생할 수 있다. 예를 들어 malloc, calloc을 사용해 메모리를 할당하면 프로그램이 종료되거나 free()를 호출하기 전까지는 메모리가 해제되지 않는다.
메모리가 부족해지면 OS에 시스템콜을 통해 직접 요청하여 더 많은 메모리 영역을 할당받으며, 이러한 특성으로 stack에 비해서 더 큰 메모리를 사용할 수 있다. 하지만 단점으로는 메모리 선언이 stack에 비해서 느리다.
객체 레퍼런스가 지칭하는 객체는 일반적으로 heap에 저장된다. 그래서 객체가 사용하는 메모리의 크기는 변할 수 있다.
스택 오버플로우 Stack Overflow
스택은 고정된 크기를 가지기 때문에 무한히 많은 데이터를 저장할 수 없다. 스택이 할당된 크기를 초과하면 Stack Overflow가 발생하며 이는 프로그램의 오류를 야기한다. 재귀함수가 반복에서 많이 쓰이면 문제가 생기는 원인이 여기에 있다. 재귀함수는 호출 될 때마다 스택 프레임을 추가하기 때문에 계속해서 호출되면 스택에 변수가 점점 쌓여서 stack overflow가 발생한다.