스와핑
메모리에 있던 내용을 보조기억장치로 잠시 내보냈다가, 다시 필요해지면 도로 들여오는 일입니다. 물리 메모리에 다 올려둘 수 없을 만큼 프로그램이 많을 때 운영체제가 이렇게 자리를 만듭니다. 대신 내보낸 것을 다시 쓰려는 순간, 메모리를 읽는 일 하나가 저장장치를 다녀오는 일로 바뀝니다.
상세
마트에서 늘 집던 물건이 매대에 없으면 직원에게 물어봅니다. 직원이 창고에 다녀오는 동안 카트를 세워 두고 기다리게 됩니다. 어떤 물건이 매대에서 빠져 창고에 들어가 있는지는 매대 앞에 서서 손을 뻗어 보기 전에는 알 수 없습니다.
스와핑은 물리 메모리에 있던 내용을 보조기억장치에 마련해 둔 자리로 내보내고, 그 내용이 다시 필요해지면 물리 메모리로 도로 들여오는 일입니다. 내보내는 쪽을 스왑 아웃, 들여오는 쪽을 스왑 인이라고 부릅니다. 내보낸 것을 담아 두는 자리는 스왑 영역이라고 부릅니다.
무엇을 내보내는지가 갈립니다. 파일에서 읽어 온 내용은 원본이 그 파일에 그대로 있습니다. 자리가 모자라면 그냥 버립니다. 다시 필요해지면 파일에서 도로 읽으면 됩니다. 반면 프로그램의 스택이나 힙처럼 어느 파일도 뒷받침하지 않는 내용은 버리면 사라집니다. 이런 내용을 저장장치에 적어 두었다가 되찾는 것이 스와핑입니다.
내보내는 단위는 바뀌었습니다. 초기에는 프로세스 하나를 통째로 내리고 통째로 올렸습니다. 자리를 만들려면 프로세스 전체가 메모리 밖으로 나가야 했습니다. 지금은 페이지 단위로 합니다. 프로그램이 통째로 나가지 않습니다. 한동안 손대지 않은 페이지만 골라 나갑니다. 그래서 스와핑과 페이징을 나란히 두고 말하는 자리가 많습니다.
내보낸 페이지를 프로그램이 다시 건드리면 페이지 폴트가 납니다. 운영체제가 그 자리에서 스왑 영역의 내용을 물리 메모리로 다시 올리고, 프로그램은 멈췄던 명령부터 이어서 돕니다. 프로그램 입장에서는 자기 페이지가 나갔다 왔다는 사실이 보이지 않습니다. 보이는 것은 그 접근 하나가 유독 오래 걸렸다는 것뿐입니다.
배경
한 대의 컴퓨터에 여러 프로그램을 같이 띄우면, 그 프로그램들이 요구하는 메모리의 합이 물리 메모리 용량을 넘는 일이 흔합니다. 이때 선택지는 둘뿐입니다. 새 프로그램을 아예 못 띄우게 막거나, 이미 올라와 있는 것 중 지금 안 쓰는 것을 어딘가로 치우거나.
치울 곳이 될 수 있는 것이 보조기억장치입니다. 용량이 물리 메모리보다 훨씬 큽니다. 전원이 나가도 내용이 남습니다. 대신 읽고 쓰는 데 걸리는 시간이 물리 메모리보다 훨씬 깁니다. 그러니 자주 쓰는 것을 여기 두면 곤란하고, 지금 안 쓰는 것만 골라 두어야 값이 맞습니다.
그래서 당장 쓰지 않는 내용을 저장장치에 맡겨 두고 비운 자리를 다른 프로그램에 내주는 방식이 나왔습니다. 맡겼다가 되찾는 이 맞바꿈을 스와핑이라고 부릅니다. 프로그램이 보기에 쓸 수 있는 메모리는 물리 메모리보다 넓어 보이고, 그 넓이의 실체 일부가 스왑 영역입니다.
대가
얻는 것은 물리 메모리 용량을 넘는 요구를 받아내는 능력입니다. 내주는 것은 메모리 접근 시간의 예측 가능성입니다. 스와핑을 켠 시스템에서는 어떤 메모리 접근 하나가 저장장치 입출력으로 바뀔 수 있습니다. 그게 언제인지는 코드를 보고 알 수 없습니다.
Linux 커널 문서는 메모리를 회수하는 과정을 두 단계로 적습니다. 여유 페이지가 첫 번째 문턱인 low watermark 아래로 내려가면, 할당 요청이 kswapd 데몬을 깨웁니다. kswapd 는 메모리 페이지를 비동기로 훑습니다. 담고 있는 내용이 다른 곳에도 있는 페이지는 그냥 풀어 줍니다. 그렇지 않은 더티 페이지는 뒷받침 저장장치로 내보냅니다. 여기까지는 할당을 요청한 쪽이 기다리지 않습니다.
메모리 사용이 더 늘어 두 번째 문턱인 min watermark 에 닿으면 직접 회수로 넘어갑니다. 커널 문서는 이때 요청을 채울 만큼 페이지가 회수될 때까지 할당이 멈춰 선다고 적습니다. 스와핑의 대가가 드러나는 자리가 여기입니다. 메모리를 달라고 한 코드가 저장장치 입출력이 끝나기를 기다립니다.
flowchart TD
A["할당 요청"] -->|"여유 페이지 넉넉"| B["여유 페이지에서 바로 내줌"]
A -->|"여유 페이지가 low watermark 아래"| C["kswapd 를 깨워 비동기로 회수"]
A -->|"메모리 사용이 min watermark 에 닿음"| D["직접 회수 — 할당이 멈춰 선다"]
C --> E["다른 곳에도 있는 페이지는 해제"]
C --> F["더티 페이지는 스왑 아웃"]
조절 손잡이가 있습니다. 커널 문서는 vm.swappiness 를 스와핑과 파일시스템 페이징의 대략적인
상대 입출력 비용을 정의하는 값이라고 적습니다. 0에서 200 사이를 받고 기본값은 60입니다.
100이면 둘의 입출력 비용을 같다고 보고 페이지 캐시와 스왑 대상 페이지에 같은 메모리 압력을
겁니다. 값이 낮을수록 스왑 입출력이 비싸다는 뜻이고, 높을수록 싸다는 뜻입니다. 0으로 두면 여유
페이지와 파일 기반 페이지의 합이 해당 존의 high watermark 아래로 내려가기 전까지 커널이 스왑을
시작하지 않습니다.
한쪽으로 밀 때와 다른 쪽으로 밀 때가 각각 값을 치릅니다. 커널 문서는 메모리 압박 아래에서 파일시스템 입출력 패턴이 스왑의 무작위 입출력보다 효율적인 경향이 있다고 적습니다. 그러면서도 최적값은 실험이 필요하고 작업 부하에 따라 달라진다고 못 박습니다. zram·zswap 처럼 스왑이 메모리 안에 있거나 스왑 장치가 파일시스템보다 빠른 구성에서는 100을 넘는 값도 고려할 수 있다고 적습니다.
아예 끄라고 권고하는 쪽도 있습니다. Elasticsearch 공식 문서는 스와핑을 어떤 대가를 치르더라도 피하라고 적습니다. 근거로 드는 것은 자바 가상 머신 힙이나 그 실행 페이지까지 디스크로 밀려날 수 있다는 점입니다. 그러면 가비지 컬렉션이 밀리초 대신 분 단위로 이어질 수 있습니다. 노드가 응답을 늦게 하거나 클러스터에서 떨어져 나갈 수도 있습니다. 이 문서는 복원력 있는 분산 시스템에서는 운영체제가 노드를 죽이게 두는 편이 더 효과적이라고 적습니다.
끄는 쪽도 값을 치릅니다. 스왑이 없으면 메모리가 모자랄 때 저장장치로 미룰 자리가 없습니다.
노드를 죽게 두는 편이 낫다는 그 문장이 곧 끄는 쪽이 치르는 값입니다. 같은 문서가 중간
선택지도 나란히 적어 둡니다. 리눅스에서 sysctl 값 vm.swappiness 를 1로 두는 방법입니다.
이렇게 하면 커널이 스왑하려는 경향이 줄어 정상적인 상황에서는 스와핑으로 이어지지 않아야 합니다.
다만 비상 상황에서는 시스템 전체가 여전히 스왑할 수 있습니다.
예시
/proc/meminfo 의 스왑 필드
리눅스는 시스템의 메모리 사용 통계를 /proc/meminfo 파일로 내놓습니다. free(1) 명령이 물리
메모리와 스왑 양쪽의 남은 양과 쓴 양을 보고할 때 이 파일을 읽습니다. 스왑과 직접 관련된 필드는
셋입니다.
SwapTotal 쓸 수 있는 스왑 공간의 총량
SwapFree 지금 쓰이지 않고 있는 스왑 공간의 양
SwapCached 한 번 스왑 아웃됐다가 도로 스왑 인됐지만 스왑 파일에도 그대로 남아 있는 메모리
SwapCached 가 따로 있는 이유를 man page 가 적어 둡니다. 메모리 압박이 심해도 이 페이지들은
이미 스왑 파일에 있으므로 다시 스왑 아웃할 필요가 없고, 그만큼 입출력이 절약됩니다.
swapon 명령과 시스템콜
swapon 은 페이징과 스와핑이 일어날 장치를 지정하는 명령입니다. swapoff 는 지정한 장치와
파일에서 스와핑을 멈춥니다.
swapon [options] [specialfile...]
swapoff [-va] [specialfile...]
man page 는 swapon 호출이 보통 시스템 부팅 스크립트 안에서 일어나 모든 스왑 장치를 사용
가능하게 만든다고 적습니다. 그러면 페이징과 스와핑 활동이 여러 장치와 파일에 걸쳐 번갈아
일어납니다.
같은 이름의 시스템콜이 커널 인터페이스 쪽에 있습니다.
#include <sys/swap.h>
int swapon(const char *path, int swapflags);
int swapoff(const char *path);
CAP_SYS_ADMIN 능력을 가진 특권 프로세스만 이 함수들을 부를 수 있습니다. 대상 파티션이나
경로는 mkswap(8) 로 미리 준비해 두어야 합니다.
스왑 영역이 여럿이면 우선순위가 붙습니다. -p, --priority 옵션이 0에서 32767 사이의 값을
받습니다. 숫자가 클수록 우선순위가 높습니다. /etc/fstab 의 옵션 칸에 pri=값 을 적어 두면
swapon -a 가 그 값을 씁니다. 값을 안 정하면 커널이 음수를 기본으로 씁니다. 스왑 페이지는
우선순위가 높은 영역부터 할당됩니다. 높은 쪽이 다 차야 낮은 쪽을 씁니다. 우선순위가 같은 영역이
여럿이고 그것이 지금 쓸 수 있는 가장 높은 우선순위라면, 그들 사이에서 라운드 로빈으로 나눠 씁니다.
Windows 페이지 파일
같은 개념이 다른 운영체제에서는 다른 이름과 다른 크기 정책으로 나타납니다. Windows 는 이 자리를 페이지 파일이라고 부릅니다. Microsoft 문서는 페이지 파일 크기가 시스템 크래시 덤프 설정 요구사항과 시스템 커밋 차지의 최대 사용량에 달려 있고, 두 가지 모두 시스템마다 고유하므로 페이지 파일 크기도 일반화할 수 없다고 적습니다.
시스템 커밋 차지는 시스템 커밋 한도를 넘을 수 없습니다. 이 한도는 물리 메모리와 모든 페이지 파일을 합한 값입니다. 페이지 파일이 하나도 없으면 커밋 한도는 설치된 물리 메모리보다 조금 작아집니다.
크래시 덤프 설정마다 요구하는 최소 페이지 파일 크기가 다릅니다.
Small memory dump (256 KB) 1 MB
Kernel memory dump 커널 가상 메모리 사용량에 따라 다름
Complete memory dump 물리 메모리 1배 + 257 MB
Automatic memory dump 커널 가상 메모리 사용량에 따라 다름
Automatic memory dump 가 기본으로 켜져 있습니다. 이 설정은 처음에 작은 페이징 파일 크기를
고릅니다. 대부분의 경우 커널 메모리를 담기에 충분한 크기입니다.
관련 항목
스왑 회수 때 갈리는 메모리 종류
익명 메모리 · 페이지 캐시 · 더티 페이지
익명 메모리를 만들어 내는 통로
메모리 회수를 이루는 구성 요소
kswapd · 직접 회수 · 워터마크 · 페이지 회수 · 워킹셋
스왑 공간이 놓이는 저장소
스왑 영역 · 스왑 파티션 · 스왑 파일 · 페이지 파일 · zram · zswap
스왑을 켜고 끄고 들여다보는 인터페이스
swapon · swapoff · vm.swappiness · mkswap · /etc/fstab · /proc/meminfo · free · CAP_SYS_ADMIN
스와핑을 실행하는 운영체제 계층
스와핑이 검색엔진 노드에서 일으키는 오류·장애
Elasticsearch · 가상 머신 · 가비지 컬렉션 · 노드 · 클러스터 · 분산 시스템
메모리 고갈에 대응하는 정책과 장치
오버커밋 · OOM killer · 시스템 커밋 차지
스와핑과 헷갈리는 이웃 개념
다른 이름: swapping · 스왑