Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

메모리 레이아웃: 스택(Stack)

지금까지는 소유권과 참조가 겉으로 어떻게 작동하는지, 즉 ’무엇을 할 수 있고 없는지’를 중심으로 살펴보았습니다. 이제는 한 걸음 더 들어가 속을 들여다볼 차례입니다. 바로 메모리(Memory) 이야기입니다.

스택(Stack)과 힙(Heap)

컴퓨터 구조나 프로그래밍을 공부하다 보면 **스택(Stack)**과 **힙(Heap)**이라는 말을 자주 듣게 됩니다. 이들은 프로그램이 데이터를 저장하기 위해 사용하는 서로 다른 성격의 메모리 공간입니다.

먼저 스택부터 알아봅시다.

스택(Stack)

스택LIFO(Last In, First Out, 후입선출) 구조를 가진 데이터 저장소입니다. 함수를 호출하면 스택 맨 위에 새로운 **스택 프레임(Stack Frame)**이 쌓이는데, 여기에는 함수의 인자, 지역 변수, 그리고 관리를 위한 정보들이 담깁니다. 함수 실행이 끝나고 값을 반환하면, 해당 스택 프레임은 스택에서 깔끔하게 제거됩니다1.

+-----------------+
| func1용 프레임  |
+-----------------+
        |
        | func2가
        | 호출됨
        v
+-----------------+
| func2용 프레임  |
+-----------------+
| func1용 프레임  |
+-----------------+
        |
        | func2가
        | 반환됨
        v
+-----------------+
| func1용 프레임  |
+-----------------+

성능 면에서 스택의 메모리 할당과 해제는 매우 빠릅니다. 항상 스택의 맨 위에서 데이터를 넣고(Push) 빼기(Pop) 때문에, 빈 메모리 공간을 찾아 헤맬 필요가 없거든요. 또한 메모리가 조각조각 나뉘는 ‘단편화(Fragmentation)’ 걱정도 없습니다. 스택은 하나의 연속된 메모리 덩어리니까요.

Rust에서의 스택 활용

Rust는 데이터를 스택에 자주 할당합니다. 함수 인자로 u32를 쓰시나요? 그 32비트는 스택에 들어갑니다. i64 타입의 지역 변수를 만드셨나요? 그 64비트 역시 스택 차지입니다. 이런 타입들은 크기가 미리 정해져 있어서, 컴파일 시점에 스택 공간을 얼마나 준비해야 할지 정확히 알 수 있기 때문에 아주 효율적으로 작동합니다.

std::mem::size_of 함수

std::mem::size_of 함수를 사용하면 특정 타입이 스택에서 차지하는 실제 크기를 바이트 단위로 확인할 수 있습니다.

예를 들어 u8 타입의 경우:

// 이 생소한 모양의 구문(`::<u8>`)은 나중에 자세히 설명해 드릴게요.
// 지금은 '아, 이런 게 있구나' 하고 넘어가셔도 괜찮습니다.
assert_eq!(std::mem::size_of::<u8>(), 1);

u8은 8비트, 즉 1바이트이므로 결과값이 1이 나오는 것이 당연하겠죠?

Exercise

The exercise for this section is located in 03_ticket_v1/08_stack


  1. 함수가 너무 깊게 중첩되어 호출되면, 스택 프레임이 계속 쌓이다가 결국 준비된 메모리 공간을 넘쳐버릴 수 있습니다. 이를 스택 오버플로(Stack Overflow)라고 부릅니다.