가상 메모리와 메모리 압축
가상 메모리는 무엇이고 메모리 압축은 어떤 방식으로 동작할까?
가상 메모리 개요
운영체제는 사용자 프로세스에게 실제 물리 메모리보다 훨씬 큰 주소 공간을 제공하기 위해 가상 메모리를 사용합니다. 가상 메모리는 물리 메모리와 디스크를 결합해 마치 무한한 메모리가 존재하는 것처럼 보이게 하며, 각 프로세스는 독립된 주소 공간을 갖습니다.
프로세스가 사용하는 주소는 논리 주소(logical address)이며, 이를 물리 주소로 매핑(mapping)하는 과정을 주소 변환(address translation)이라고 합니다. 이 과정은 하드웨어인 MMU(Memory Management Unit)를 통해 수행됩니다.
프로세스의 주소 공간은 페이지 단위로 나뉘며, 페이지는 물리 메모리의 프레임에 매핑됩니다. 프레임은 일정한 크기를 가진 메모리 블록이며, 운영체제가 페이지 테이블을 통해 어떤 논리 페이지가 어떤 물리 프레임에 매핑되었는지를 관리합니다.
프레임 할당 전략
운영체제는 다수의 프로세스가 동시에 실행될 수 있도록 각 프로세스에 일정 수의 프레임을 분배합니다. 하지만 물리 메모리는 제한되어 있기 때문에, 어떤 기준으로 몇 개의 프레임을 분배할지에 대한 전략이 필요합니다.
최소로 할당해야 할 프레임 수
프레임 수가 줄어들면 페이지 폴트율은 증가하고 프로세스 실행은 늦어지게 됩니다. 명령어 수행 도중 페이지 폴트가 발생하면 해당 명령어는 중단되고, 필요한 페이지를 가져온 후 다시 처음부터 재실행됩니다.
이처럼 페이지 폴트가 너무 잦으면 성능 저하가 발생하며, 일정 이상의 프레임을 보장하지 않으면 실행 자체가 어려워질 수 있습니다.
최소 프레임 수는 다음에 따라 결정됩니다:
- CPU 아키텍처가 허용하는 최대 명령어 길이
- 하나의 명령어 실행에 필요한 최대 페이지 수
프레임 할당 알고리즘
균등 할당 (Equal Allocation)
- 전체 가용 프레임 수를 프로세스 수로 나눔
- 간단하지만 프로세스마다 요구하는 메모리 양이 다를 수 있음
비례 할당 (Proportional Allocation)
- 각 프로세스의 크기(페이지 수) 비율에 따라 프레임을 분배
- 메모리 요구량이 큰 프로세스는 더 많은 프레임을 받을 수 있음
다중 프로그래밍 수준이 높아질수록 각 프로세스가 받을 수 있는 프레임 수는 줄어듭니다. 반대로 다중 프로그래밍 수준이 낮아지면 프로세스당 프레임 수가 증가합니다.
전역 교체 vs 지역 교체
전역 교체 (Global Replacement)
- 전체 시스템의 모든 프레임을 대상으로 희생 페이지를 선택
- 우선순위 높은 프로세스가 다른 프로세스의 프레임을 가져갈 수 있음
- 시스템 자원을 보다 유연하게 사용할 수 있음
- 다만, 다른 프로세스의 성능이 영향을 받을 수 있음
지역 교체 (Local Replacement)
- 각 프로세스에게 할당된 프레임 범위 내에서만 교체
- 다른 프로세스에 영향 주지 않음
- 할당된 프레임 수가 부족하면 자체 성능 저하
리눅스에서는 전역 교체 방식을 선호하며, 2차 기회 알고리즘과 같은 LRU 근사 방식을 사용합니다. 심각한 메모리 부족 시에는 FIFO 정책으로 후퇴합니다.
또한, 물리 메모리가 일정 임계값 이하로 내려가면 커널은 리퍼(ripper) 루틴을 실행하여 모든 프로세스에서 프레임을 회수하려 시도하며, 마지막 수단으로 OOM Killer를 실행해 메모리를 해방합니다.
NUMA와 비균등 메모리 접근
NUMA(Non-Uniform Memory Access) 구조에서는 여러 CPU가 각자의 로컬 메모리를 가지고 있습니다. CPU는 자신의 로컬 메모리에 빠르게 접근할 수 있지만, 다른 CPU의 메모리에 접근할 경우 느려집니다.
운영체제는 페이지 폴트 발생 시, 해당 프로세스가 실행 중인 CPU와 가장 가까운 NUMA 노드에서 프레임을 할당합니다. 리눅스는 NUMA 노드별로 별도의 가용 프레임 리스트를 유지하여, 스레드가 자신의 로컬 노드에서 메모리를 할당받도록 보장합니다.
스래싱과 그 해결 방법
스래싱의 원인
스래싱(thrashing)은 프로세스가 계산보다 페이지 교체에 더 많은 시간을 소모하는 현상입니다. 이때 프로세스는 계속해서 페이지 폴트를 발생시키며, 페이지를 메모리에 올리기 위한 작업만 반복되고 실질적인 작업을 거의 수행하지 못하게 됩니다.
스래싱은 다중 프로그래밍 수준이 과도하게 높아졌을 때, 즉 너무 많은 프로세스가 동시에 실행되어 각 프로세스에 할당된 프레임 수가 부족할 때 발생합니다.
운영체제가 CPU 이용률이 낮다고 판단하여 더 많은 프로세스를 실행하면, 전역 페이지 교체 알고리즘 하에서는 실행 중인 프로세스들이 서로의 프레임을 빼앗기 시작하게 됩니다. 이로 인해 페이지 교체가 빈번해지고, CPU는 페이지 로딩만 기다리느라 유휴 상태가 되며, 전체 시스템 성능이 급락하게 됩니다.
지역성 모델
프로세스는 실행 중 메모리의 특정 부분만 집중적으로 참조하는 경향이 있습니다. 이를 지역성(locality)이라 하며, 크게 시간 지역성과 공간 지역성으로 나뉩니다.
- 시간 지역성(Temporal locality): 최근에 사용된 데이터가 가까운 미래에 다시 참조됨
- 공간 지역성(Spatial locality): 현재 참조된 데이터 근처에 있는 데이터가 곧 참조됨
이러한 지역성의 존재로 인해, 운영체제는 캐시, 페이지 교체 알고리즘, 메모리 압축 등의 정책을 보다 효율적으로 설계할 수 있습니다.
프로세스는 실행 중 여러 지역(Locality)으로 구성되며, 이들은 서로 겹칠 수 있고 시간에 따라 변경되기도 합니다.
작업 집합 모델
작업 집합(Working Set)은 일정 시간 간격 Δ 동안 참조된 페이지 집합을 의미합니다. 이는 프로세스가 실제로 필요로 하는 메모리의 크기를 추정하는 데 사용됩니다.
운영체제는 각 프로세스의 작업 집합 크기를 추적하여, 해당 크기만큼의 프레임을 할당해 줍니다. 이 방식은 각 프로세스가 필요한 만큼의 메모리를 유지함으로써 스래싱을 방지하고, 동시에 가능한 많은 프로세스를 실행시킬 수 있는 장점을 제공합니다.
작업 집합 모델은 다음과 같은 단계로 운영됩니다:
- 일정시간 동안의 참조 기록을 유지
- 이 안에 포함된 고유 페이지의 개수를 작업 집합으로 간주
- 이 개수를 기반으로 동적으로 프레임 수 조절
페이지 폴트 빈도 기반 조절
페이지 폴트 빈도(Page Fault Frequency, PFF)는 운영체제가 스래싱을 판단하고 방지하는 데 사용할 수 있는 또 다른 메커니즘입니다.
운영체제는 각 프로세스의 페이지 폴트 발생 간격을 관찰하여 상한과 하한을 설정하고, 그에 따라 다음과 같이 대응합니다:
- 페이지 폴트 빈도가 너무 높다 → 프레임을 더 할당
- 페이지 폴트 빈도가 너무 낮다 → 프레임 일부 회수하여 다른 프로세스에 재할당
이 방식은 작업 집합 모델보다 구현이 간단하며, 동적 환경에 유연하게 적응할 수 있는 장점이 있습니다.
메모리 압축
메모리 압축의 정의
메모리 압축은 디스크 스왑을 최소화하거나 대체하기 위해 사용되는 기법으로, 페이지를 압축하여 메모리 내에 더 많은 데이터를 보존하는 방식입니다. 이는 RAM 내에 압축된 데이터를 저장하고 필요 시 압축을 해제하여 접근하는 방식으로 작동합니다.
페이징 기법에서는 수정된 페이지를 스왑 공간으로 내보내야 하지만, 메모리 압축을 사용하면 여러 프레임을 압축하여 하나의 프레임에 저장함으로써 I/O 비용 없이 메모리 사용량을 줄일 수 있습니다.
이 방식은 특히 모바일 환경(Android, iOS)에서 유용합니다. 모바일 시스템은 전통적인 스왑 장치를 지원하지 않는 경우가 많고, 디스크 I/O가 제한적이기 때문에 메모리 압축은 핵심적인 메모리 관리 전략이 됩니다.
동작 방식 및 구조
메모리 압축은 일반적으로 다음과 같은 순서로 동작합니다:
- 프로세스가 페이지 폴트를 일으키고, 운영체제가 페이지 교체를 결정함
- 디스크로 스왑을 하기 전에, 해당 페이지를 압축
- 압축된 페이지는 메모리 내의 별도 압축 영역(Compressed Cache)에 저장됨
- 이후 해당 페이지가 다시 필요하면, 압축을 해제하여 메모리에 다시 로드
압축 알고리즘은 속도와 효율성을 고려하여 선택되며, 주로 LZ4, LZO 등이 사용됩니다. 이 알고리즘들은 비교적 빠르며 압축률이 적당하여 메모리와 CPU의 균형을 맞추는 데 유리합니다.
압축을 수행하면 물리적 프레임 수는 변하지 않지만, 압축된 페이지가 차지하는 공간이 줄어들기 때문에 결과적으로 더 많은 데이터를 메모리에 유지할 수 있습니다.
리눅스: zswap vs zram
리눅스 커널에서는 메모리 압축을 위해 두 가지 주요 메커니즘을 제공합니다.
- zswap
- 스왑 아웃될 페이지를 디스크에 쓰기 전에 메모리에서 압축하여 보관
- 압축된 페이지는 압축 캐시에 저장되며, 페이지 접근 시 디스크 대신 이 캐시에서 복원됨
- 스왑을 줄여 시스템 반응 속도를 개선
- zram
- 압축된 블록 장치를 메모리 상에 만들어 이를 스왑 디바이스로 사용
- 실제 디스크를 사용하지 않고 RAM 내에서 압축된 스왑을 구현함
- 안드로이드나 저사양 임베디드 시스템에서 널리 사용됨
zswap은 디스크 스왑을 보조하는 캐시 형태이고, zram은 디스크 스왑 자체를 메모리 기반으로 대체하는 구조입니다.
Windows와 Apple의 메모리 압축
Windows 10부터는 메모리 압축 기능이 기본 활성화되어 있습니다. 페이지가 스왑되기 전에 운영체제는 이를 압축하여 RAM에 유지하며, 작업 관리자를 보면 시스템 프로세스가 사용하는 메모리가 높게 나타나는 것이 이 때문입니다.
애플의 macOS 역시 메모리 압축을 지원합니다. OS X Mavericks부터 도입되었으며, 압축 알고리즘으로 WKdm(Wire Kernel Data Compression)을 사용합니다.
장단점 정리
메모리 압축의 장점:
- 디스크 스왑 I/O 감소 → 반응 속도 향상
- RAM에 더 많은 페이지를 유지 가능
- SSD 쓰기 횟수 감소로 수명 연장 효과
- 스왑 파티션이 없는 환경에서 유용함 (모바일 등)
단점:
- 압축과 해제 과정에서 CPU 자원 사용 증가
- 압축 효과가 낮은 데이터에선 효율성 감소
- 압축 영역도 결국 메모리를 소모함
- 과도한 압축은 시스템 병목을 유발할 수 있음
다른 메모리 관리 기법과의 비교
| 구분 | 전통 스왑 | 메모리 압축 | OOM Killer |
|---|---|---|---|
| 저장 위치 | 디스크 | 메모리 | 프로세스 종료 |
| 속도 | 느림 | 빠름 | 즉시 해방 |
| 장점 | 메모리 절약 | 응답성 향상 | 극단적 해결 |
| 단점 | I/O 병목 | CPU 소모 | 데이터 손실 가능 |
메모리 압축은 스왑을 완전히 대체하기보다는 보완하는 역할을 하며, OOM Killer와 함께 시스템의 안정성과 성능을 높이기 위한 복합 전략의 일부로 사용됩니다.


