메모리는 컴퓨터 시스템의 가장 핵심적인 자원 중 하나로, 이를 효과적으로 관리하는 것은 프로그램의 성능과 안정성에 결정적인 영향을 미칩니다. 메모리 관리의 중요성에 대해 대부분의 프로그래머들은 인지하고 있지만, 그 뒤에 숨겨진 복잡한 메커니즘들을 완전히 이해하는 것은 쉬운 일이 아닙니다. 이 글을 통해 이 복잡한 메커니즘을 동적 메모리 할당 함수인 'malloc'으로 시작하여, 실제 운영체제가 메모리를 어떻게 할당하고 관리하는지, 또한 CPU 캐싱이 메모리 접근에 어떤 영향을 미치는지를 살펴보며 하나하나 풀어나가보겠습니다. 이 글에서 소개하고 있는 키워드의 하나 하나가 깊은 내용을 담고 있기에, 깊게 들어가기보다는 개념과 흐름을 알아가보는 형식으로 진행하겠습니다.
<aside> 💡 malloc 을 제작하며 학습과 함께 작성된 글입니다. 일부 잘못된 내용이 있을 수 있습니다.
</aside>
메모리 할당에 들어가기에 앞서, 잠시 프로세스의 메모리 구조에 대해서 아주 가볍게 살펴봅시다. 각 프로세스는 각자의 가상 메모리 주소 공간을 할당받습니다. 그리고 아래와 같은 구조를 가지게 됩니다. (아주 단순화해서 표현한 모습입니다.)
아주 단순화한 메모리 구조
프로세스의 메모리 구조는 크게 TEXT, DATA, STACK, HEAP 영역으로 나뉩니다. 프로그램이 실행되어 프로세스가 메모리에 올라오게 되면 위와 같은 구조로 데이터들이 나열되어있다는 의미입니다.
(아래 설명은 GPT의 도움과 함께 작성되었습니다. 저보다 이 친구의 설명이 더 깔끔하네요…)
해당 영역에는 프로그램의 실행 코드가 저장됩니다. 주로 기계어 명령어로 구성되어 있습니다. 텍스트 영역은 읽기 전용이며, 실행 시간에 변경할 수 없습니다.
해당 영역에는 전역 변수와 정적 변수가 저장됩니다. 이 영역은 프로그램의 수명 동안 계속 존재하며, 두 부분으로 나뉘어집니다.
이 영역에는 함수 호출과 관련된 정보가 저장됩니다. 이 정보에는 지역 변수, 함수 매개 변수, 반환 주소 등이 포함됩니다. 스택은 LIFO(Last In, First Out) 방식으로 작동합니다. 즉, 마지막에 들어온 항목이 가장 먼저 나갑니다. 함수 호출이 이루어질 때마다 새로운 스택 프레임이 생성되며, 함수 호출이 완료되면 해당 스택 프레임이 삭제됩니다.