페이지 폴트가 지나치게 잦아 CPU가 일보다 페이징에 시간을 더 쓰는 상태.
다중 프로그래밍 정도를 높이면
- 처음엔 CPU 이용률이 올라간다 (대기 중인 프로세스를 대신 돌리므로)
- 어느 지점을 넘으면 급락한다 ← 스래싱
프로세스당 프레임이 워킹셋보다 작아지면
- 페이지를 올리자마자 다른 페이지 때문에 쫓겨난다 → 무한 반복
워킹셋
프로세스가 최근 일정 시간 동안 참조한 페이지의 집합
- 이 집합이 메모리에 다 못 올라가면 스래싱이 시작된다
- 워킹셋 크기만큼 프레임을 보장하는 것이 대책
구역성(locality) 이 있기 때문에 워킹셋이라는 개념이 성립한다 — 프로그램은 특정 시점에 메모리의 일부만 집중적으로 쓴다.
증상과 진단
-
CPU 이용률은 낮은데 디스크가 계속 돈다
-
응답이 극도로 느려진다
-
스왑 in/out 이 폭증한다
-
vmstat 1 → si/so 열(스왑 in/out)이 계속 0 이 아니면 의심
대응
-
다중 프로그래밍 정도를 낮춘다 (일부 프로세스를 스왑 아웃)
-
물리 메모리를 늘린다
-
워킹셋이 큰 프로세스를 분리한다
-
컨테이너 환경에서는 메모리 limit 이 낮으면 같은 일이 벌어진다
- cgroup 이 압박을 감지해 OOM Killer 를 부르기도 한다
스와핑과 페이징을 구분한다
- 스와핑 — 프로세스 '전체' 를 디스크로 내보낸다 (고전적 정의)
- 페이징 — '페이지 단위' 로 내보낸다 (현대 시스템)
리눅스의 swap 파티션은 이름과 달리 페이지 단위로 동작한다 "스왑이 일어난다" = 페이지가 디스크로 내려간다는 뜻이다
숫자 감각 — 왜 이렇게 치명적인가
메모리 접근 약 100 ns
SSD 랜덤 읽기 약 100 µs → 1,000 배
HDD 랜덤 읽기 약 10 ms → 100,000 배
페이지 폴트율이 1,000 번에 1 번만 되어도 평균 접근 시간이 두 배가 된다
- '조금 부족한 것' 과 '스래싱' 사이가 매우 가파른 이유
JVM 과 스래싱
힙이 물리 메모리보다 크면 GC 가 스래싱을 유발한다
- GC 는 힙 전체를 훑으므로 구역성이 없다
- 스왑된 페이지를 전부 다시 올린다 → 정지 시간 폭발
그래서 힙은 물리 메모리 안에 들어가야 하고, 컨테이너에서는 MaxRAMPercentage 로 limit 에 맞춘다
면접 함정
- ❌ "다중 프로그래밍을 높일수록 좋다" → 임계를 넘으면 처리율이 급락한다.
- ❌ "스래싱 = 메모리 부족" → 부족의 결과지만, 핵심은 워킹셋을 못 담는 것이다.