12편. 페이징과 스와핑 - 운영체제가 메모리 부족을 해결하는 방법
📚 목차
1. 페이징과 스와핑 - 메모리 절약의 핵심 기술
2. 페이지 폴트(Page Fault)의 발생과 처리 과정
3. 페이지 교체 알고리즘 - FIFO vs LRU 비교
4. OOM(Out-of-Memory) 상황과 OOM Killer의 작동 원리
✔ 마무리 - 메모리 위기 상황에 대처하는 운영체제
운영체제는 단순히 ‘메모리가 부족하면 프로그램을 실행할 수 없다’는 식으로 동작하지 않습니다. RAM이 부족해도 여러 프로그램을 동시에 실행할 수 있는 핵심 기술이 바로 페이징(Paging)과 스와핑(Swapping)입니다.

이 글에서는 제한된 메모리로도 다양한 프로그램을 실행할 수 있게 해주는 이 두 기술을 중심으로,
🔸 페이지 폴트가 발생하는 원리
🔸 페이지 교체 알고리즘
🔸 최악의 상황인 OOM(Out-of-Memory) 대응 방식
까지 단계적으로 살펴보겠습니다.
1. 페이징과 스와핑 - 메모리 절약의 핵심 기술
운영체제가 관리하는 자원 중 가장 빠르지만 가장 부족한 것이 바로 주기억장치(RAM)입니다. 컴퓨터는 여러 프로그램을 동시에 실행해야 하며, 이 중 일부는 실행 중이고, 일부는 대기하거나 백그라운드에서 잠시 멈춘 상태일 수 있습니다.
이처럼 다양한 상태의 프로그램을 제한된 메모리 공간 안에서 효율적으로 운용하기 위해, 운영체제는 가상 메모리(Virtual Memory)라는 개념을 사용합니다.
가상 메모리는 사용자에게는 넓은 메모리를 제공하는 것처럼 보이지만, 실제로는 일부 데이터만 메모리에 올리고, 나머지는 디스크에 저장해 두었다가 필요할 때 불러오는 방식입니다.
이 가상 메모리를 효과적으로 구현하기 위해 운영체제는 두 가지 핵심 기술인 페이징(Paging)과 스와핑(Swapping)을 사용합니다.
🟦 페이징(Paging)이란?
페이징은 프로그램을 고정된 크기의 '페이지(Page)'라는 단위로 나눈 후, 실제 메모리에서는 동일한 크기의 '프레임(Frame)'에 적재하여 관리하는 방식입니다.
이때 전체 프로그램을 메모리에 올릴 필요 없이, 당장 실행에 필요한 페이지만 올리면 되기 때문에, 훨씬 많은 프로그램을 동시에 다룰 수 있습니다.
🔸 예를 들어 100MB 크기의 프로그램이 있다고 해도, 그 중 10MB만 실제로 실행 중이라면 나머지 90MB는 디스크에 그대로 두고, 필요한 10MB 페이지만 메모리에 올리면 되는 것입니다.
🔸 이렇게 하면 메모리의 낭비를 줄이고, 병렬 실행도 가능해집니다.
이 과정을 통해 사용자는 메모리 용량의 한계를 느끼지 않고, 여러 프로그램을 동시에 효율적으로 실행할 수 있습니다.

페이징에서는 CPU가 논리 주소를 생성하며, 이 주소는 페이지 번호와 오프셋(해당 페이지 안 위치)으로 구성됩니다.
운영체제는 페이지 번호를 페이지 테이블에서 찾아, 그 페이지가 메모리 어디에 있는지(기준 주소) 확인합니다.
그리고 이 기준 주소에 오프셋을 더해 실제 메모리 주소(물리 주소)를 계산합니다.
🟦 스와핑(Swapping)이란?
페이징이 프로그램을 잘게 나누는 전략이라면, 스와핑은 프로그램 전체를 대상으로 하는 메모리 해제 기법입니다.
🔸 메모리가 부족할 경우, 당장 실행되지 않는 프로세스 전체를 디스크로 내보내고, 그 공간에 다른 프로세스를 올리는 방식입니다.
🔸 나중에 해당 프로세스가 다시 필요해지면, 디스크에서 메모리로 다시 불러오는 과정을 거칩니다.

스와핑은 특히 멀티태스킹 환경에서 메모리가 심각하게 부족할 때 주로 동작하며, 성능에 큰 영향을 줄 수 있기 때문에 운영체제가 신중하게 결정합니다.
🟦 비유로 이해하기
이 개념을 책상과 책에 비유하면 좀 더 직관적으로 이해할 수 있습니다.
책상이 너무 작아서 여러 책을 한꺼번에 올릴 수 없을 때, 당장 필요한 책만 책상 위에 올려두고, 나머지 책은 사물함에 보관하게 됩니다.
이때,
🔸 필요한 챕터만 골라 책상에 올리는 방식이 페이징(Paging)이며,
🔸 책 한 권 전체를 사물함과 책상 사이에서 교체하는 방식이 스와핑(Swapping)입니다.
페이징은 사용 빈도가 높은 조각만 선별해서 들고 오는 방식이고, 스와핑은 전체 책(프로세스)을 사물함과 책상 사이에서 완전히 교체하는 방식인 셈입니다.
2. 페이지 폴트(Page Fault)의 발생과 처리 과정
운영체제는 모든 프로그램을 한꺼번에 메모리에 올려둘 수 없기 때문에, 필요한 부분만 골라서 메모리에 적재하는 가상 메모리(Paging) 방식을 사용합니다.
이 과정에서 프로그램이 아직 메모리에 없는 데이터에 접근하려 할 때, 시스템은 페이지 폴트(Page Fault)라는 예외 상황을 처리하게 됩니다.
🟦 페이지 폴트(Page Fault)란?
페이지 폴트(Page Fault)란, CPU가 어떤 메모리 주소에 접근했을 때, 해당 주소가 현재 메인 메모리에 존재하지 않는 경우 발생하는 인터럽트(예외)입니다. 운영체제는 이를 감지하고 필요한 조치를 자동으로 수행합니다.
예시:
책상에서 과제를 하다가 필요한 자료가 책장에 있을 경우, 그 책을 꺼내서 책상 위에 올려야 하죠.
이때 ‘책상 위에 없음 → 책장에서 가져오기’ 과정을 운영체제는 페이지 폴트 처리라고 부릅니다.
🟦 처리 흐름 요약
🔸[프로세스 실행 중]
→ 프로그램이 실행되며 특정 명령어를 수행합니다.
↓
🔸[페이지 접근 요청]
→ CPU가 특정 메모리 주소의 데이터에 접근하려고 합니다.
↓
🔸[메모리에 없음 → Page Fault]
→ 해당 데이터가 현재 메모리에 없기 때문에 페이지 폴트 예외가 발생합니다.
↓
🔸[운영체제가 디스크에서 페이지 읽어오기]
→ 운영체제가 디스크(보조 기억장치)에서 필요한 페이지를 찾아 읽어옵니다.
↓
🔸[메모리에 적재 + 페이지 테이블 갱신]
→ 읽어온 페이지를 메모리에 올리고, 페이지 테이블을 최신 상태로 업데이트합니다.
↓
🔸[프로세스 재개]
→ 중단된 위치부터 프로그램 실행이 다시 이어집니다.
실제로 페이지 폴트는 정상적인 작동 과정이며, 가상 메모리를 가능하게 하는 핵심 메커니즘입니다.
🟦 페이지 폴트가 과도하면? (Thrashing 현상)
페이지 폴트 자체는 정상이지만, 너무 자주 발생하면 시스템 성능이 급격히 떨어질 수 있습니다.
🔸 디스크는 메모리에 비해 수십~수천 배 느린 속도를 가지고 있기 때문에,
🔸 매번 디스크 접근이 필요하다면 프로세스 실행 속도는 크게 지연됩니다.
이런 상황을 스래싱(Thrashing)이라고 하며, 이는 스왑과 페이지 교체가 지나치게 자주 발생하여 CPU가 실제 작업보다 페이지 교체에 더 많은 시간을 소모하는 상태를 의미합니다.
따라서 운영체제는 메모리 크기, 페이지 교체 전략, 스왑 설정 등을 조정하여 페이지 폴트를 최소화하고 성능을 최적화하려고 합니다.
3. 페이지 교체 알고리즘 - FIFO vs LRU 비교
페이징을 사용하는 시스템에서 메모리가 가득 찼을 때, 새로운 페이지를 올리기 위해 기존 페이지 중 어떤 것을 제거할지 결정해야 합니다.
이 때 사용하는 전략이 페이지 교체 알고리즘(Page Replacement Algorithm)입니다.
대표 알고리즘은 다음과 같습니다:
🔸 FIFO (First-In First-Out): 가장 먼저 올라온 페이지를 제거
🔸 LRU (Least Recently Used): 가장 오래 사용되지 않은 페이지를 제거
🔸 Optimal: 앞으로 가장 늦게 참조될 페이지를 제거 (이론적 최선)
🟦 시나리오 예제: FIFO vs LRU
상황 가정:
▸ 브라우저에서 탭 4개(A, B, C, D)를 순서대로 엽니다.
▸ RAM에는 동시에 최대 3개의 페이지만 올릴 수 있습니다.
▸ 사용 순서: A → B → C → A → D → A
이 상황에서 FIFO와 LRU는 어떤 선택을 할까요?
📌 FIFO(First-In, First-Out)
가장 먼저 메모리에 올라온 페이지를 가장 먼저 제거하는 방식으로, 구조는 단순하지만, 자주 사용하는 페이지가 먼저 제거될 수 있어 비효율적입니다.
| 시간 | 요청 | 메모리 | 페이지폴트 | 제거페이지 | 설명 |
| T1 | A | [A, -, -] | ✅ | - | A 처음 적재 |
| T2 | B | [A, B, -] | ✅ | - | B 적재 |
| T3 | C | [A, B, C] | ✅ | - | C 적재 |
| T4 | A | [A, B, C] | ❌ | - | A 이미 있음 |
| T5 | D | [D, B, C] | ✅ | A | 가장 먼저 들어온 A 제거, D 적재 |
| T6 | A | [A, B, C] | ✅ | D | D 제거, A 다시 적재 (다시 페이지 폴트 발생) |
📌 LRU(Least Recently Used)
가장 오랫동안 사용되지 않은 페이지를 제거하는 방식으로, 최근 사용 이력을 반영해, 일반적으로 FIFO보다 더 효율적인 성능을 냅니다.
| 시간 | 요청 | 메모리 | 페이지폴트 | 제거페이지 | 설명 |
| T1 | A | [A, -, -] | ✅ | - | A 처음 적재 |
| T2 | B | [A, B, -] | ✅ | - | B 적재 |
| T3 | C | [A, B, C] | ✅ | - | C 적재 |
| T4 | A | [A, B, C] | ❌ | - | A 최근에 재사용됨 |
| T5 | D | [A, C, D] | ✅ | B | 가장 오래 안 쓰인 B 제거, D 적재 |
| T6 | A | [A, C, D] | ❌ | - | A 이미 있음 (유지됨), 페이지 폴트 없음 |
이번 시나리오에서 FIFO는 가장 먼저 들어온 A를 먼저 제거하기 때문에, A를 다시 사용할 때마다 페이지 폴트가 발생해 총 5번의 페이지 폴트가 발생했습니다.
반면 LRU는 최근에 사용된 A를 유지하고, 오랫동안 사용되지 않은 B를 제거하여 4번만 페이지 폴트가 발생했습니다.
이처럼 LRU는 실제 사용 패턴을 반영해 더 효율적인 페이지 교체 결과를 보여줍니다.
4. OOM 상황과 OOM Killer의 작동 원리
운영체제는 한정된 메모리 자원(RAM)을 효율적으로 사용하기 위해 다양한 메커니즘을 동원합니다.
예를 들어, 자주 사용하지 않는 데이터를 스왑 공간(Swap Space)으로 내보내거나, 오래된 페이지를 제거하여 새로운 데이터를 위한 공간을 확보하는 방식이 그것입니다.
하지만 경우에 따라서는 이러한 전략으로도 더 이상 메모리를 확보할 수 없는 극단적인 상황이 발생합니다.
이것이 바로 OOM(Out-of-Memory, 메모리 부족) 상태입니다.
🟦 언제 OOM 상황이 발생할까?
다음 세 가지 조건이 동시에 충족되면, 시스템은 OOM 상황에 직면하게 됩니다.
🔸 RAM 사용률이 100%에 도달
🔸 스왑 공간도 모두 사용됨
🔸 운영체제가 더 이상 제거할 수 있는 페이지가 없음
→ 어떤 페이지도 교체 불가 → 메모리 할당 실패 → 시스템 정지 위험
이때는 어떤 페이지도 교체하거나 내보낼 수 없는 상태이기 때문에, 새로운 페이지를 메모리에 적재하려는 모든 시도는 실패하게 됩니다.
이 상태에서 시스템이 그대로 멈춘다면, 전체 서비스 중단이나 커널 패닉으로 이어질 수 있기 때문에, 운영체제는 이를 방지하기 위한 최후의 수단을 준비해 둡니다.
🟦 OOM Killer란?
Linux 운영체제에서는 OOM 상황을 감지하면, 자동으로 OOM Killer(Out-of-Memory Killer)라는 커널 기능이 작동합니다.
🔸이는 가장 많은 메모리를 사용 중인 프로세스를 강제로 종료시켜 메모리를 확보하는 방식입니다.
🔸즉, 시스템 전체 다운을 막기 위해 일부 프로세스를 희생하는 전략입니다.
운영체제는 단순히 메모리 사용량만을 기준으로 삼지 않습니다.
대신 oom_score라는 지표를 계산하여, 다음과 같은 요소를 종합적으로 고려합니다.
🔸 프로세스가 사용하는 전체 메모리 양
🔸 프로세스의 우선순위 (nice, oom_score_adj)
🔸 백그라운드/전면 앱 여부, 시스템 중요도 등
운영체제는 가장 적절한 프로세스를 선정해 즉시 종료하고, 해당 로그를 남깁니다.
Out of memory: Kill process 1243 (python3) score 923 or sacrifice child
Killed process 1243 (python3) total-vm:312345kB, anon-rss:305000kB, file-rss:456kB
이 예시는 python3 프로세스가 높은 oom_score를 기록하여 OOM Killer에 의해 강제 종료된 것을 보여줍니다.
🟦 OOM 예방 전략 (리눅스 - 실무 적용)
운영체제는 OOM 상황을 방지하기 위해 다양한 사전 예방 전략을 제공합니다. 다음은 실무 환경에서 흔히 활용되는 주요 방법들입니다.
✔️ 1. 프로세스 메모리 제한 설정
운영체제 또는 컨테이너 환경에서 프로세스 단위로 메모리 사용량을 제한하면, 비정상적인 메모리 폭주를 방지할 수 있습니다.
🔸 ulimit: Linux의 사용자별 자원 제한 설정.
예: ulimit -v 1048576 → 최대 가상 메모리 사용량 1GB 제한
🔸 cgroups (Control Groups): 리눅스 커널 기능으로, CPU, 메모리, I/O 등 리소스를 그룹 단위로 제어
예: /sys/fs/cgroup/memory/my_app/에 메모리 제한 값 지정
🔸 Docker 메모리 제한: 컨테이너별 제한 가능
예: docker run -m 512m my_app → 최대 512MB로 제한
→ 특정 프로세스나 컨테이너가 시스템 전체를 먹는 상황을 사전에 차단할 수 있습니다.
✔️ 2. 중요 서비스 보호 설정
OOM Killer는 메모리 부족 시 자동으로 종료할 대상을 선정하는데, 이때 oom_score_adj 값을 통해 중요 프로세스를 보호하거나 덜 중요한 프로세스를 우선 종료하도록 조정할 수 있습니다.
🔸 oom_score_adj = -1000: 종료 대상에서 제외 (최우선 보호)
🔸 oom_score_adj = 1000: 가장 먼저 종료될 후보
# 중요 서비스 보호
echo -1000 > /proc/1234/oom_score_adj
→ DB, 웹서버, 모니터링 도구 등 핵심 프로세스의 강제 종료를 방지할 수 있습니다.
✔️3. 스왑 정책 조절 (swappiness 설정)
Linux에서는 swappiness라는 커널 파라미터를 통해, 시스템이 RAM을 얼마나 아껴 쓰고 스왑을 얼마나 활용할지 비율을 조절할 수 있습니다.
🔸 swappiness = 0: 가능한 한 스왑 사용 억제
🔸 swappiness = 60: 기본값, RAM과 스왑 균형 활용
🔸 swappiness = 100: 스왑 우선 사용
sysctl vm.swappiness=10
→ 스왑 남용을 막고 페이지 폴트 발생 빈도를 줄이는 전략입니다.
✔ 마무리 - 메모리 위기 상황에 대처하는 운영체제
운영체제는 항상 부족한 메모리 자원을 최대한 활용하기 위해 다음 전략을 사용합니다:
🔸페이징: 프로그램을 잘게 쪼개 필요한 부분만 메모리에 적재
🔸스와핑: 전체 프로세스를 디스크로 내보내고 공간 확보
🔸페이지 교체 알고리즘: FIFO, LRU로 효율적 선택
🔸OOM Killer: 비상 상황에서 시스템 보호
이러한 기술 덕분에 우리는 제한된 메모리에서도 여러 프로그램을 동시에 안정적으로 실행할 수 있습니다.
※ 게시된 글 및 이미지 중 일부는 AI 도구의 도움을 받아 생성되거나 다듬어졌습니다.
'1.시스템&인프라 > 개발 입문자를 위한 운영체제' 카테고리의 다른 글
| 14편. HDD, SSD, NVMe 완전 정복 – 저장장치 속도와 수명의 모든 것 (0) | 2025.11.21 |
|---|---|
| 13편. 캐시 메모리로 성능 최적화하기 – CPU를 더 빠르게 만드는 방법 (0) | 2025.11.21 |
| 11편. 단편화란 무엇인가? – 메모리는 남는데 왜 실행이 안 될까? (0) | 2025.11.20 |
| 10편. 가상 메모리란? – RAM보다 많은 프로그램을 실행하는 비결 (1) | 2025.11.20 |
| 9편. CPU 스케줄링 성능 지표 - 성능은 어떻게 비교할까? (0) | 2025.11.20 |