함수가 호출될 때마다 스택 프레임이 생기고, 여기에 매개변수, 지역변수, 반환 주소가 들어간다.
void foo(int x) { // 매개변수 x → 스택에 저장
int y = 10; // 지역변수 y → 스택에 저장
int z = x + y; // 연산 결과도 스택 프레임에서 처리
// foo 종료 시점 → x, y, z 자동 해제
}
함수 호출 시 스택 프레임이 만들어지고, 종료되면 자동으로 사라진다.
스택 관련 알고리즘 문제를 풀 때를 기억해보면 이해하기 쉬운데 괄호 관련 문제에서 스택을 사용하는 느낌으로 밑의 코드를 보면 된다.
void function() {
int a[10]; // 40바이트
{
int b[20]; // 80바이트 (최대 120바이트 지점)
{
int c[5]; // 20바이트 (최대 140바이트 지점)
} // c 해제
} // b 해제
int d[15]; // 60바이트 (b, c 공간 재사용)
}
// 컴파일러: "이 함수는 최대 140바이트가 필요하구나"
블록(스코프)마다 변수 수명이 명확하므로, 컴파일 시 필요한 크기를 계산할 수 있다.
스택이 무한이라서 호출 스택을 늘리다보면 굉장히 코드적인 부분에서 좋지 않을 수 있다.
호출 depth가 늘어나면 만약 버그가 난다고 했을 때 어디서 난 건지 확인할 수 없고 유지보수가 힘들게 된다.
"동적 배열을 크게 잡으면 되지 않을까?"
ArrayList 를 통해 자동으로 크기를 늘려줘서 힙의 역할을 일부 대체할 수는 있다 하지만 존재 목적 자체가 다른 것을 생각해야 한다.
스택은 함수 범위 (scope)와 강하게 연결되어 있어서 함수 종료 후에도 존재해야 하는 데이터를 스택 변수만으로는 관리가 불가하다!
힙 없이 스택만 사용해서 스레드 간 공유 객체를 만든다고 생각을 해봤을 때, 데이터를 각 스택에 복사해야하고 복잡한 관리가 필요하다.. 힙처럼 공유 메모리 역할을 수행할 수 없음!
"만일 스택이 매우 커질 수 있다면 힙은 불필요할까?" 에 대해서 답변을 한다면 NO 라고 답할 것 같다.
그 이유는 힙이 가지고 있는 특성 동적 메모리 할당, 공유 메모리 역할, 접근의 자유도 를 스택은 구조적인 한계로 불가능 하기 때문이다.
스택은 함수 호출과 지역 변수 관리용!
힙은 동적 객체와 멀티스레드 공유용!
널널한 개발자 TV (youtube)
https://www.youtube.com/watch?v=9TSojdIr8Q0