RAM과 주소 공간

본 포스트는 인프런 “[CS 기술면접 2] 말이 트이는 운영체제” 강의를 학습하며 정리한 내용을 다룹니다.
1. RAM과 주소 공간
RAM
RAM(Random Access Memory)은 주기억 장치를 구성하는 대표적인 메모리로, 실행 중인 프로그램과 데이터를 저장하여 CPU가 빠르게 접근할 수 있도록 합니다.
- 빠른 읽기·쓰기 속도: CPU가 실행 중인 프로그램과 데이터에 빠르게 접근할 수 있는 메모리
- 휘발성 메모리: 전원 공급이 중단되면 저장된 데이터가 사라지는 특성
- 임의 접근(Random Access): 데이터가 저장된 위치와 관계없이 거의 일정한 시간에 접근 가능
- 다중 프로그램 실행: RAM 용량이 클수록 더 많은 프로그램을 메모리에 적재하여 동시에 실행 가능
주소 공간(Address Space)

주소 공간은 메모리의 각 위치를 식별하기 위해 사용하는 주소의 범위를 의미합니다. 프로그램이 메모리에 저장된 데이터에 접근하려면 해당 데이터가 위치한 주소를 알아야 합니다.
메모리 주소는 크게 물리 주소와 논리 주소로 구분됩니다.
- 물리 주소(Physical Address)
- 실제 RAM에서 데이터가 저장된 물리적인 위치를 나타내는 주소
- 논리 주소(Logical Address)
- CPU가 프로그램을 실행하면서 참조하는 주소
- 실제 물리 메모리의 위치와 독립적으로 구성되는 주소
- MMU를 통해 물리 주소로 변환한 후 실제 메모리에 접근
논리 주소가 필요한 이유
프로그램이 실행될 때마다 항상 동일한 물리 메모리 위치에 적재되는 것은 아닙니다. 메모리 사용 상황에 따라 프로그램이 적재되는 물리 주소는 달라질 수 있습니다.
논리 주소를 사용하면 프로그램은 실제 물리 주소를 직접 알 필요 없이 자신에게 할당된 주소 공간을 기준으로 메모리에 접근할 수 있습니다.
- 물리 주소와의 독립성: 프로그램이 다른 물리 메모리 위치에 적재되더라도 동일한 논리 주소를 사용하여 접근 가능
- 메모리 보호: 프로그램마다 독립적인 논리 주소 공간을 제공하고, 주소 변환 및 접근 권한 검사를 통해 다른 프로그램의 메모리 침범 방지
2. MMU의 주소 변환
MMU(Memory Management Unit)는 CPU가 사용하는 논리 주소를 실제 메모리의 물리 주소로 변환하는 하드웨어 장치입니다.
가장 기본적인 베이스·한계 레지스터 기반 주소 변환 방식에서는 두 레지스터를 사용하여 메모리 접근을 관리합니다.
- 베이스 레지스터(Base Register): 현재 프로그램이 적재된 물리 메모리의 시작 주소를 저장
- 한계 레지스터(Limit Register): 프로그램이 접근할 수 있는 메모리 주소 범위의 크기를 저장
주소 변환 과정

CPU가 프로그램의 데이터를 조회하기 위해 논리 주소를 사용하면, MMU는 이를 물리 주소로 변환하여 실제 RAM에 접근합니다.
베이스 레지스터를 이용한 주소 변환은 다음과 같습니다.
예시: CPU가 프로그램 B의 논리 주소 50에 접근하는 과정
- 프로그램 B의 논리 주소 범위: 0부터 80 미만
- CPU가 접근하려는 논리 주소: 50
- 프로그램 B의 베이스 레지스터: 200
- 프로그램 B의 한계 레지스터: 80
MMU는 프로그램 B의 베이스 레지스터 값인 200에 논리 주소 50을 더하여 실제 물리 주소를 계산합니다.
따라서 CPU가 요청한 논리 주소 50은 물리 주소 250으로 변환됩니다.
접근 검증
MMU는 주소를 변환하는 것뿐만 아니라, 프로그램이 자신에게 할당된 메모리 범위를 벗어나 접근하지 못하도록 검사합니다.
- 접근 범위 확인: 논리 주소가 0 이상이고 한계 레지스터 값보다 작은지 검사
- 범위 초과 시 접근 차단: 허용된 범위를 벗어나면 하드웨어 예외(Trap)를 발생시켜 잘못된 메모리 접근 차단
예시: 프로그램 B가 허용되지 않은 메모리에 접근하는 경우
- 프로그램 B의 베이스 레지스터: 200
- 프로그램 B의 한계 레지스터: 80
- 프로그램 B가 접근할 수 있는 물리 주소 범위: 200 이상 280 미만
- CPU가 접근하려는 논리 주소: 100
CPU가 논리 주소 100에 접근을 요청하면, 단순히 베이스 레지스터를 더했을 때의 주소는 다음과 같습니다.
하지만 논리 주소 100은 한계 레지스터 값인 80보다 크므로 허용된 범위를 벗어납니다. 따라서 MMU는 접근을 차단하고 예외를 발생시킵니다.
즉, 실제 물리 주소 300에 접근하기 전에 잘못된 주소 요청임을 확인합니다.
3. 프로그램 실행 시 메모리 최적화 기법
프로그램을 실행할 때 모든 코드와 데이터를 처음부터 메모리에 적재할 필요는 없습니다. 실행에 필요한 시점에만 일부 코드나 데이터를 적재하거나 외부 라이브러리를 연결하면 메모리 사용량을 줄일 수 있습니다.
대표적인 기법으로 동적 로딩과 동적 연결이 있습니다.
동적 로딩(Dynamic Loading)
동적 로딩은 프로그램 전체를 한꺼번에 메모리에 적재하지 않고, 실행 중 필요한 모듈이나 데이터를 필요한 시점에 메모리로 불러오는 기법입니다.
전통적인 동적 로딩 방식에서는 개발자가 필요한 코드나 데이터를 언제 적재하고 해제할지 명시적으로 제어할 수 있습니다.
- 동작 방식: 프로그램 실행 중 필요한 모듈이나 데이터만 메모리에 적재
- 개발자의 제어: 필요한 시점에 데이터를 로드하거나 해제하도록 코드 작성
예시: 게임에서 지역별 맵 데이터 로딩
게임에서 플레이어가 새로운 지역에 진입할 때마다 해당 지역의 맵과 텍스처 데이터를 메모리에 불러오는 방식입니다.
- 숲 지역에 진입하면 숲에 필요한 맵과 텍스처를 로딩
- 사막 지역으로 이동하면 사막에 필요한 데이터를 로딩
- 더 이상 사용하지 않는 숲 지역 데이터는 메모리에서 해제
장점
- 필요한 데이터만 적재하여 메모리 사용량 감소
- 초기 실행 시 불필요한 데이터 로딩을 줄여 시작 속도 향상
- 필요한 모듈이나 데이터를 실행 중 교체할 수 있어 유연한 업데이트 가능
단점
- 데이터의 로딩(Load)과 해제(Unload) 시점 관리 필요
- 불필요한 메모리를 해제하지 못하면 메모리 누수 발생 위험
- 메모리 적재 및 해제 로직에 따른 코드 복잡도 증가
메모리 누수(Memory Leak)란?
메모리 누수란 프로그램이 더 이상 사용하지 않는 메모리를 적절히 해제하지 않아, 불필요한 메모리 점유가 계속되는 현상입니다.
게임에서 새로운 지역에 진입할 때마다 맵과 텍스처를 동적으로 로딩하는 상황을 예로 들어보겠습니다.
- 플레이어가 숲에 진입하면서 숲 데이터를 200MB 로딩합니다.
- 사막으로 이동하면서 사막 데이터를 300MB 로딩합니다.
- 숲 데이터는 더 이상 필요하지 않지만, 메모리에서 해제하는 처리를 빠뜨렸습니다.
현재 필요한 데이터는 사막의 300MB뿐이지만, 실제로는 숲 데이터 200MB가 남아 있어 총 500MB의 메모리를 점유하게 됩니다.
이러한 현상이 지역을 이동할 때마다 반복되면 메모리 사용량이 계속 증가할 수 있습니다.
동적 연결(Dynamic Linking)
동적 연결은 프로그램 실행에 필요한 외부 라이브러리의 함수나 코드를 실행 시점에 연결하는 기법입니다.
동적 연결은 정적 연결(Static Linking)과 비교하면 이해하기 쉽습니다.
- 정적 연결(Static Linking): 빌드 과정에서 필요한 라이브러리 코드를 실행 파일에 포함
- 동적 연결(Dynamic Linking): 라이브러리 코드를 실행 파일에 직접 포함하지 않고, 실행 시 동적 링커를 통해 연결
동적 연결은 운영체제의 동적 링커·로더에 의해 처리될 수 있으며, 라이브러리는 프로그램 시작 시점이나 실제 필요한 시점에 적재될 수 있습니다.
예시: 웹 브라우저에서 플러그인 사용
웹 브라우저(Chrome 등)에 비디오 플레이어와 같은 플러그인이 설치되어 있다고 가정하겠습니다.
브라우저는 해당 기능을 항상 사용할 필요가 없으므로, 사용자가 비디오 플레이어 기능을 요청하는 시점에 필요한 플러그인 코드를 메모리에 적재하고 연결할 수 있습니다.
이는 필요한 시점에 외부 코드를 불러오는 예시로, 동적 로딩과 동적 연결의 개념을 함께 이해할 수 있습니다.
장점
- 라이브러리 코드를 실행 파일에 직접 포함하지 않아 실행 파일 크기 축소
- 호환성이 유지되는 라이브러리 업데이트 시 프로그램 재컴파일 없이 변경 사항 반영 가능
- 여러 프로그램이 동일한 공유 라이브러리를 사용하여 메모리 사용 효율 증가