Ray Book
운영체제 기초

메모리 관리

가상 메모리의 마법, 페이징, 페이지 테이블, TLB, 페이지 폴트를 시각화합니다

csosmemoryvirtual-memorypagingpage-fault

물리 메모리 vs 가상 메모리

컴퓨터의 RAM은 한정된 자원입니다. 4GB RAM에서 각 프로세스가 자기만의 메모리 공간을 독립적으로 사용하려면 어떻게 해야 할까요?

OS는 가상 메모리라는 추상화 계층을 제공합니다. 각 프로세스는 자기만의 연속적인 주소 공간을 가진다고 착각 합니다. 실제로는 OS가 가상 주소를 물리 주소로 변환해줍니다.

구분물리 메모리가상 메모리
주소실제 RAM 칩의 주소프로세스가 보는 논리적 주소
크기RAM 용량에 제한이론적으로 무한 (디스크까지 확장)
공유직접 접근 시 충돌 위험프로세스 간 격리 보장
관리하드웨어가 직접 주소 지정OS + MMU가 변환

가상 메모리의 핵심 이점:

  • 격리 : 프로세스 A가 프로세스 B의 메모리에 접근할 수 없음
  • 추상화 : 물리 메모리의 단편화를 프로세스가 신경 쓸 필요 없음
  • 확장 : 물리 RAM보다 큰 주소 공간 사용 가능 (디스크를 보조 저장소로 활용)

페이징 (Paging)

가상 메모리를 구현하는 가장 보편적인 방법이 페이징 입니다. 메모리를 고정 크기 블록으로 나눕니다.

  • 페이지 (Page) : 가상 메모리의 블록 (보통 4KB)
  • 프레임 (Frame) : 물리 메모리의 블록 (페이지와 같은 크기)

가상 주소는 두 부분으로 구성됩니다.

가상 주소 = [페이지 번호] + [오프셋]

예) 4KB 페이지, 가상 주소 0x3014
  페이지 번호 = 0x3 (3번째 페이지)
  오프셋      = 0x014 (페이지 내 위치, 0~4095)

고정 크기를 사용하면 외부 단편화가 발생하지 않습니다. 가변 크기를 쓰는 세그멘테이션과의 가장 큰 차이점입니다.

페이지 테이블 (Page Table)

페이지 번호를 프레임 번호로 매핑하는 자료구조가 페이지 테이블 입니다. 각 프로세스마다 고유한 페이지 테이블을 가집니다.

프로세스 A의 페이지 테이블:
  페이지 0 --> 프레임 5  (valid=1)
  페이지 1 --> 프레임 8  (valid=1)
  페이지 2 --> 디스크     (valid=0, 물리 메모리에 없음)
  페이지 3 --> 프레임 2  (valid=1)

각 항목(PTE, Page Table Entry)에는 여러 비트가 포함됩니다.

비트역할
Valid해당 페이지가 물리 메모리에 있는지
Dirty페이지가 수정되었는지 (스왑 아웃 시 디스크에 다시 써야 하는지)
Reference최근에 접근되었는지 (페이지 교체 알고리즘에 사용)
Protection읽기/쓰기/실행 권한

TLB (Translation Lookaside Buffer)

페이지 테이블은 메모리에 있으므로, 매번 메모리 접근마다 페이지 테이블을 읽으면 성능이 크게 나빠집니다. x86-64의 4단계 페이지 테이블에서는 한 번의 데이터 접근에 최대 4번의 추가 메모리 접근이 필요하므로, 최악의 경우 약 5배 느려집니다.

이 문제를 해결하기 위해 CPU 안에 TLB 라는 고속 캐시를 둡니다. TLB는 최근 사용된 페이지 테이블 항목을 저장합니다.

구분TLB페이지 테이블
위치CPU 내부 (하드웨어)메인 메모리
크기64~1024 항목수백만 항목 가능
속도1~2 사이클수백 사이클
히트율보통 95% 이상전체 주소 공간을 표현 (단, 일부 엔트리는 valid=0, 페이지 폴트)

주소 변환 흐름을 시각화로 확인합니다. 먼저 TLB에서 히트하는 경우입니다.

CPU 접근 요청단계 1 / 3
가상 주소
페이지0
오프셋12
TLB 조회
TLB
V.PageFrame
05
32
프로세스가 가상 주소 (페이지 0, 오프셋 12)에 접근합니다. CPU는 이 가상 주소를 물리 주소로 변환해야 합니다. 먼저 TLB(Translation Lookaside Buffer)를 확인합니다.

다음은 TLB에서 미스가 발생하여 페이지 테이블을 조회하는 경우입니다.

CPU 접근 요청단계 1 / 4
가상 주소
페이지1
오프셋4
TLB 조회
TLB
V.PageFrame
05
32
프로세스가 가상 주소 (페이지 1, 오프셋 4)에 접근합니다. 먼저 TLB를 확인합니다.

페이지 폴트 (Page Fault)

페이지 테이블에서 valid 비트가 0이면 해당 페이지가 물리 메모리에 없다는 뜻입니다. 이때 페이지 폴트 가 발생합니다.

페이지 폴트 처리 과정:

  1. CPU가 인터럽트(트랩)를 발생시켜 OS 커널에 제어를 넘김
  2. OS가 디스크의 스왑 영역에서 해당 페이지를 찾음
  3. 빈 프레임이 있으면 그곳에 적재, 없으면 기존 페이지를 교체 (스왑 아웃)
  4. 페이지 테이블과 TLB를 갱신
  5. 원래 명령어를 재실행

아래 시각화에서 페이지 폴트가 발생하고 디스크에서 페이지를 적재하는 과정을 확인하세요.

CPU 접근 요청단계 1 / 5
가상 주소
페이지2
오프셋7
TLB 조회
TLB
V.PageFrame
05
32
프로세스가 가상 주소 (페이지 2, 오프셋 7)에 접근합니다. 먼저 TLB를 확인합니다.

디스크 I/O는 메모리 접근보다 약 100,000배 느립니다. 그래서 페이지 폴트가 자주 발생하면 성능이 급격히 저하됩니다.

접근 시간 비교:
  TLB Hit       ~1ns       (1~2 사이클)
  TLB Miss      ~10-100ns  (페이지 테이블 워크, x86-64에서 최대 4단계 메모리 접근)
  Page Fault    ~10ms      (디스크 I/O, 약 10,000,000ns)

페이지 교체 알고리즘

물리 메모리가 가득 찼을 때 새로운 페이지를 적재하려면 기존 페이지 하나를 내보내야 합니다. 어떤 페이지를 교체할지 결정하는 것이 페이지 교체 알고리즘 입니다.

FIFO (First-In First-Out)

가장 먼저 들어온 페이지를 교체합니다. 구현이 단순하지만 성능이 좋지 않습니다.

접근 순서: 1, 2, 3, 4, 1, 2 (프레임 3개)

  접근 1: [1, -, -]       페이지 폴트
  접근 2: [1, 2, -]       페이지 폴트
  접근 3: [1, 2, 3]       페이지 폴트
  접근 4: [4, 2, 3]       페이지 폴트 (1 교체, 가장 오래됨)
  접근 1: [4, 1, 3]       페이지 폴트 (2 교체)
  접근 2: [4, 1, 2]       페이지 폴트 (3 교체)

  폴트 횟수: 6

FIFO는 Belady의 모순 이 발생할 수 있습니다 -- 프레임 수를 늘렸는데 오히려 페이지 폴트가 증가하는 현상입니다.

LRU (Least Recently Used)

가장 오랫동안 사용되지 않은 페이지를 교체합니다. "최근에 사용된 페이지는 곧 다시 사용될 가능성이 높다"는 시간적 지역성 (temporal locality) 에 기반합니다.

접근 순서: 1, 2, 3, 4, 1, 2 (프레임 3개)

  접근 1: [1, -, -]       페이지 폴트
  접근 2: [1, 2, -]       페이지 폴트
  접근 3: [1, 2, 3]       페이지 폴트
  접근 4: [4, 2, 3]       페이지 폴트 (1 교체, 가장 오래 미사용)
  접근 1: [4, 1, 3]       페이지 폴트 (2 교체)
  접근 2: [4, 1, 2]       페이지 폴트 (3 교체)

  폴트 횟수: 6

접근 순서: 1, 2, 3, 1, 4, 2 (프레임 3개)

  접근 1: [1, -, -]       페이지 폴트
  접근 2: [1, 2, -]       페이지 폴트
  접근 3: [1, 2, 3]       페이지 폴트
  접근 1: [1, 2, 3]       히트 (1은 이미 있음, 최근 사용으로 갱신)
  접근 4: [1, 4, 3]       페이지 폴트 (2 교체, 가장 오래 미사용)
  접근 2: [1, 4, 2]       페이지 폴트 (3 교체)

  폴트 횟수: 5

LRU는 성능이 좋지만, 정확한 구현이 비쌉니다 (매 접근마다 타임스탬프 갱신 필요). 실제 OS는 Clock 알고리즘 (Second-Chance) 같은 근사 LRU를 사용합니다.

최적 알고리즘 (OPT, Optimal)

앞으로 가장 오랫동안 사용되지 않을 페이지를 교체합니다. 이론적으로 최적이지만, 미래를 알 수 없으므로 실제로는 구현할 수 없습니다. 다른 알고리즘의 성능을 비교하는 기준으로 사용됩니다.

알고리즘장점단점실제 사용
FIFO구현 단순Belady 모순, 성능 낮음거의 안 씀
LRU시간 지역성 활용, 성능 좋음구현 비용 높음근사 버전 사용 (Clock)
OPT이론적 최적미래 예측 불가비교 기준으로만 사용

스래싱 (Thrashing)

프로세스가 실제로 사용하는 페이지 집합 (Working Set)이 물리 메모리보다 크면, 페이지 폴트가 연쇄적으로 발생합니다. 페이지를 적재하자마자 다른 페이지를 위해 내보내야 하는 상황이 반복됩니다.

이 상태를 스래싱 이라 합니다. CPU 사용률이 급락하고, 대부분의 시간을 디스크 I/O (스와핑)에 소비합니다.

정상 상태:
  CPU 사용률: 80~90%
  페이지 폴트율: 낮음 (대부분 TLB/메모리 히트)

스래싱 발생:
  CPU 사용률: 5~10% (대부분 I/O 대기)
  페이지 폴트율: 극도로 높음
  디스크 사용률: 100%

스래싱 해결 방법:

  • 프로세스 수 줄이기 : 활성 프로세스를 줄여 각자에게 충분한 메모리 할당
  • Working Set 모델 : 각 프로세스의 Working Set 크기를 추적하여, 합산이 물리 메모리를 초과하면 일부 프로세스를 일시 중단
  • 물리 메모리 추가 : 가장 직접적인 해결책

실무에서의 연결

V8 힙과 OS 메모리

JavaScript 엔진 V8의 메모리 관리는 OS 가상 메모리 위에서 동작합니다. V8이 new Object()를 실행하면:

  1. V8이 자체 힙에서 공간을 할당 (Young Generation / Old Generation)
  2. V8 힙은 OS에서 mmap()이나 VirtualAlloc()으로 확보한 가상 메모리 영역
  3. 실제 물리 메모리는 첫 접근 시점에 lazy allocation 으로 할당 (페이지 폴트 활용)
  4. V8 GC가 메모리를 회수하면 OS에 반환 (또는 재사용)
// V8 힙 크기 제한 설정 (Node.js)
// --max-old-space-size=4096  (4GB)

// 이 제한은 OS 가상 메모리의 일부를 V8이 사용하는 범위
// OS 레벨에서는 프로세스 전체의 가상 주소 공간이 훨씬 크다

V8의 GC (Garbage Collection)가 수행하는 "메모리 압축 (compaction)"은 OS 레벨의 페이지 이동과 유사합니다. V8이 힙 내에서 객체를 재배치하면, 페이지 테이블 매핑이 바뀌지는 않지만, 메모리 지역성이 개선되어 TLB 히트율과 캐시 히트율이 올라갑니다.

Chrome 메모리 탭

Chrome DevTools의 Memory 탭에서 볼 수 있는 수치들은 모두 OS 메모리 관리와 연결됩니다.

Chrome Memory 탭OS 메모리 개념
JS Heap SizeV8이 할당받은 가상 메모리 중 사용 중인 영역
Total Size프로세스의 RSS (Resident Set Size, 실제 물리 메모리 사용량)
Shallow Size객체 자체의 크기 (페이지 내 오프셋 관점)
Retained SizeGC Root에서 도달 가능한 전체 크기 (Working Set과 유사)
// Node.js에서 메모리 사용량 확인
const mem = process.memoryUsage();
console.log({
  rss: mem.rss,              // OS가 할당한 물리 메모리 (RSS)
  heapTotal: mem.heapTotal,  // V8 힙 전체 크기 (가상)
  heapUsed: mem.heapUsed,    // V8 힙 중 사용 중인 크기
  external: mem.external,    // C++ 바인딩 메모리
});
// rss > heapTotal인 이유: V8 힙 외에도 코드, 스택, C++ 객체 등이 포함

rssheapTotal의 차이가 큰 이유는, 프로세스의 메모리가 V8 힙만이 아니기 때문입니다. 코드 세그먼트, 스택, 네이티브 바인딩 등도 물리 메모리를 차지합니다.

다음 단계

이 글에서는 가상 메모리, 페이징, 페이지 테이블, TLB, 페이지 폴트, 교체 알고리즘, 스래싱을 다뤘습니다. OS가 한정된 물리 메모리 위에서 각 프로세스에게 독립적인 주소 공간을 제공하는 마법의 원리입니다. 다음 글에서는 OS의 I/O 시스템과 파일 시스템을 다루겠습니다.