페이지 부재 빈도 (PFF, Page Fault Frequency)
핵심 인사이트 (3줄 요약)
- 본질: 페이지 부재 빈도 (PFF) 알고리즘은 각 프로세스에서 발생하는 페이지 폴트(Page Fault)의 횟수(빈도)를 직접 감시하여, 폴트가 너무 잦으면 램(Frame)을 더 주고 폴트가 안 나면 램을 회수하는 동적 메모리 할당 정책이다.
- 가치: 스래싱(Thrashing)을 막기 위해 제안되었던 워킹 셋(Working Set) 모델의 극악무도한 관리 오버헤드를 해결하기 위해, "어떤 페이지를 쓰는가?"를 추적하는 대신 "얼마나 에러(Fault)가 나는가?"라는 직관적이고 가벼운 지표로 타협한 실용주의 알고리즘이다.
- 융합: 운영체제는 PFF의 상한선(Upper Bound)과 하한선(Lower Bound)을 설정하고, 모든 프로세스가 이 임계치 안에서 안정적으로 호흡하도록 메모리 파이(Pie)를 실시간으로 재분배하여 시스템 전체의 CPU 이용률을 극대화한다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
- 개념: 프로세스 실행 중 발생하는 '단위 시간당 페이지 폴트 횟수'를 측정하여, 이 빈도수(Frequency)가 상한선을 넘으면 프로세스에게 프레임을 추가 할당하고, 하한선 아래로 떨어지면 잉여 프레임을 회수하는 기법이다.
- 필요성: 워킹 셋(Working Set) 모델은 이론적으로 완벽했지만, 매번 메모리에 접근할 때마다 $\Delta$ 시간 동안의 궤적을 갱신해야 하므로 CPU가 느려터졌다. 엔지니어들은 "어차피 메모리가 모자라면 폴트가 터지니까, 복잡하게 추적하지 말고 폴트 터지는 횟수만 세자!"라는 역발상을 했다. 이는 구현이 압도적으로 가벼우면서도 워킹 셋과 거의 동일하게 스래싱을 막아내는 훌륭한 대체재였다.
- 💡 비유: 에어컨 온도를 맞출 때, 사람의 체온, 심박수, 땀 분비량을 센서로 정밀 측정해서 1도씩 조절하는 것(워킹 셋)이 아니라, 그냥 가만히 있다가 "아 더워!"라고 불평하는 횟수(Page Fault)가 1분에 3번을 넘으면 온도를 내리고, "추워"라는 말이 안 나오면 온도를 슬쩍 올리는(PFF) 눈치껏 관리하는 방식이다.
- 등장 배경: 1970년대 후반, 가상 메모리 시스템이 상용화되면서 OS의 커널 오버헤드를 줄이는 것이 지상 과제가 되었다. 이때 워킹 셋의 무거움을 비판하며 W. W. Chu 등이 제안한 PFF는 복잡한 수학적 추적을 단순한 카운터 2개(상한/하한)로 대체하며 메모리 동적 할당의 실무적 근간을 세웠다.
[페이지 부재 빈도(PFF)에 따른 OS의 동적 프레임 할당 메커니즘]
▲
Page │ 🚨 스래싱 위험 구간 (메모리 턱없이 부족!)
Fault │ ─▶ OS 조치: 이 프로세스에게 빈 프레임을 당장 더 줘라!
Rate │ (빈 프레임이 없으면 아예 이 놈을 Swap-out 시켜서 죽여라)
├───────────────────────── [ 상한선 (Upper Bound) ] ──────
│
│ ✅ 안정 구간 (메모리가 딱 알맞음)
│ ─▶ OS 조치: 건드리지 말고 현상 유지.
│
├───────────────────────── [ 하한선 (Lower Bound) ] ──────
│ 💤 낭비 구간 (메모리를 너무 많이 쥐고 있음)
│ ─▶ OS 조치: 램 낭비다! 이 프로세스에서 프레임을 뺏어와라!
▼
[다이어그램 해설] PFF는 "모든 프로세스를 상한선과 하한선 사이의 '안정 구간'에 가둬두는 것"이 목표다. 어떤 놈이 상한선을 뚫고 올라갔는데 시스템에 남은 빈 방(Free Frame)이 0개라면? OS는 가차 없이 그 놈이나 다른 만만한 놈 하나를 골라 디스크로 내쫓아(Suspend) 빈 방을 만들어낸다. 이것이 다중 프로그래밍 정도(DOM)를 조절하여 스래싱을 막는 핵심 원리다.
- 📢 섹션 요약 비유: 회사에서 부서별로 예산(프레임)을 줄 때, PFF는 "돈이 모자라요!"라고 징징대는 결재 서류(페이지 폴트)의 개수를 셉니다. 서류가 너무 많이 올라오면 예산을 늘려주고, 한 달 내내 예산 달란 소리가 없으면 "돈이 남나 보네?" 하고 예산을 깎아서 다른 부서에 줘버리는 가장 직관적인 재무부장입니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
PFF 알고리즘의 동작 사이클 (시간차 감지)
PFF는 워킹 셋처럼 "항상" 감시하지 않는다. 오직 페이지 폴트가 터진 그 찰나의 순간에만 깨어나서 계산한다.
- 폴트 발생: 스레드 A가 페이지 폴트를 일으켜 OS가 개입한다.
- 시간 계산:
현재 폴트가 터진 시간(t_current)-직전 폴트가 터졌던 시간(t_last)= **$\Delta t$ (폴트 발생 간격)**을 계산한다. - 상태 판별 및 조치:
- $\Delta t$ 가 너무 짧다 (자주 터진다 = 상한선 초과):
- "방금 전에도 터졌는데 또 터져? 메모리가 모자라군!"
- 방금 폴트가 난 그 페이지를 메모리에 올리면서, 기존에 있던 페이지를 뺏지 않고 프레임을 1개 추가해 준다 (Working Set 증가).
- $\Delta t$ 가 충분히 길다 (어쩌다 터진다 = 하한선 미만):
- "오랜만에 터졌네? 그동안 메모리 넉넉하게 잘 썼단 얘기군."
t_last부터t_current사이에 **한 번도 참조되지 않은(R비트=0) 잉여 페이지들을 모조리 회수(Free)**해 버린다 (Working Set 축소).
- $\Delta t$ 가 너무 짧다 (자주 터진다 = 상한선 초과):
워킹 셋(Working Set) vs PFF 의 구현 비용 (Overhead) 차이
왜 PFF가 실무적으로 워킹 셋을 압살했는지 비교해 보자.
| 구현 항목 | 워킹 셋 (Working Set) 모델 | PFF (Page Fault Frequency) 알고리즘 |
|---|---|---|
| 감시 타이밍 | 매번 메모리를 읽을 때마다 (혹은 아주 짧은 주기마다) | 오직 페이지 폴트(인터럽트)가 발생했을 때만! |
| 추적 자료구조 | 큐나 배열을 만들어서 최근 $K$ 번의 궤적을 1개씩 저장하고 밀어내야 함 | 단순히 이전 폴트가 터진 **'시간값(Timestamp) 1개'**만 변수에 저장하면 끝남 |
| 메모리 회수 시점 | 윈도우 $\Delta$ 를 벗어나는 순간 즉시 1개씩 회수 | 폴트 간격이 길어 폴트가 터졌을 때, 안 쓴 놈들을 뭉텅이로 한 번에 회수 |
| 시스템 부하 | 극악 (이론상으로만 존재) | 극강의 가벼움 (현실 OS에 구현 가능) |
- 📢 섹션 요약 비유: 워킹 셋은 매일 퇴근할 때마다 "오늘 안 쓴 물건 1개"를 찾아내서 버리는 피곤한 미니멀리스트입니다. PFF는 평소엔 짐을 막 쌓아두다가, 이사할 때(페이지 폴트 시)가 되어서야 "최근 1년간 안 입은 옷 다 버려!" 하고 한 방에 대청소를 하는 실용주의자입니다.
Ⅲ. 융합 비교 및 다각도 분석 (Comparison & Synergy)
지역성(Locality)의 변화 (Phase Shift)와 PFF의 반응
프로그램은 항상 일정한 메모리를 쓰지 않는다. "메인 메뉴(10MB) ─▶ 3D 게임 진입(2GB) ─▶ 로비 복귀(10MB)"처럼 성격이 변하는 **페이즈 전환(Phase Shift)**이 일어날 때 PFF는 기가 막히게 반응한다.
- 급격한 팽창기 (3D 게임 진입):
- 새로운 데이터(텍스처, 모델)를 마구 부르므로 페이지 폴트가 1초에 1,000번씩 미친 듯이 터진다.
- PFF 상한선을 뚫어버린다. OS는 프레임을 뺏지 않고 계속 무한정 던져주어 순식간에 2GB의 작업 공간(워킹 셋)을 램에 세팅해 준다.
- 안정기 (게임 플레이 중):
- 2GB 안에서 잘 놀기 때문에 폴트가 거의 안 터진다 (상한/하한 사이).
- OS는 프레임 개수를 그대로 2GB로 유지시켜 준다.
- 수축기 (로비 복귀):
- 한참 동안 폴트가 안 나다가, 새로운 메뉴를 누르며 간만에 폴트가 딱 1번 터졌다 ($\Delta t$ 가 매우 김).
- OS: "오랜만에 터졌네! 게임할 때 쓰던 2GB 데이터, 지금 하나도 안 쓰고 있지?"
- 즉시 3D 게임용 2GB 메모리를 싹 다 압수(Free)하여 다른 프로세스에게 넘겨준다.
PFF의 치명적 맹점 (Blind Spot)
PFF의 유일한 단점은 **"폴트가 안 터지면 OS가 개입을 안 한다"**는 점이다. 위 시나리오에서 게임 로비로 돌아온 뒤 사용자가 아무것도 안 하고(폴트를 1번도 안 내고) 그냥 가만히 있으면? OS는 2GB를 회수할 기회(인터럽트) 자체를 얻지 못해 2GB가 영원히 램에 묶이는 메모리 누수(Memory Hogging) 비슷한 현상이 발생할 수 있다.
- 📢 섹션 요약 비유: PFF는 '민원 기반 행정'입니다. 민원(폴트)이 빗발치면 공무원(OS)이 출동해 예산을 늘려주지만, 100억 예산을 타간 부서가 아무 민원도 없이 가만히 숨죽이고 있으면 공무원이 그 부서의 남는 예산을 뺏어올 생각조차 못 한다는 행정의 사각지대가 존재합니다.
Ⅳ. 실무 적용 및 기술사적 판단 (Strategy & Decision)
실무 시나리오
- Windows OS의 Working Set Trimming과 PFF의 융합:
- 윈도우 작업 관리자에서 프로그램의 메모리가 갑자기 확 줄어드는 것을 본 적이 있을 것이다.
- 실무 작동: 윈도우는 순수 PFF의 단점(폴트가 안 나면 회수를 못 함)을 보완하기 위해, 백그라운드 타이머 스레드(Working Set Manager)를 돌린다. 이 데몬은 1초마다 깨어나서 "얘 폴트 안 나고 메모리만 쥐고 있네?" 싶으면 PFF 하한선을 강제로 트리거하여(Trimming) 안 쓰는 낡은 페이지들을 디스크 스왑으로 확 내쫓아버린다. 이를 통해 남는 램을 극한으로 쥐어짠다.
- JVM GC (Garbage Collection)와의 유사성 및 충돌 방지:
- 자바 힙(Heap) 메모리는 JVM이 관리하지만, 그 힙 메모리를 감싸고 있는 껍데기(OS 가상 메모리)는 OS의 PFF 정책에 지배받는다.
- 장애 발생: JVM이 힙을 10GB 할당해놓고 평소에 2GB만 쓰면, OS의 PFF는 "이 자식 폴트도 안 내고 8GB나 안 쓰네?" 라며 8GB를 디스크 스왑으로 쫓아낸다(수축기). 나중에 JVM의 GC가 돌며 안 쓰던 메모리를 쓱 스캔하는 순간, 디스크로 쫓겨났던 8GB가 램으로 미친 듯이 불려 올라오며(팽창기 PFF 상한선 돌파) 수만 번의 Major Page Fault가 터져 서버가 10초간 뻗어버린다.
- 아키텍트 조치: 이 환장할 OS와 JVM의 엇박자를 막기 위해 클라우드 인프라에서는 **무조건 Swap을 끄는 것(
swapoff)**이 절대 진리로 굳어졌다. PFF가 프레임을 회수하고 싶어도 쫓아낼 디스크가 없으므로 JVM은 10GB를 영구적으로 온전히 보존받게 된다.
┌────────────────────────────────────────────────────────────────────┐
│ 가상 메모리의 과도한 팽창/수축을 막는 실무 아키텍트의 무기 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ [요구사항: 초격차 지연시간(Ultra-Low Latency)을 요하는 AI 서버] │
│ │
│ [ ❌ PFF의 자율 조절에 맡김 (운영체제 디폴트) ] │
│ - 작동: 모델 추론(Inference) 시 메모리 10GB 팽창, │
│ 쉬는 시간에 1GB로 수축됨. │
│ - 결과: 다음 추론이 들어오면 다시 10GB를 램으로 퍼오느라 │
│ 응답 시간이 10ms에서 5초로 튀는 'Jitter' 발생! │
│ │
│ [ ✅ mlock() / HugePages 강제 고정 (아키텍트의 결단) ] │
│ - 작동: OS에게 "내 10GB 메모리는 상한/하한 계산하지 말고 │
│ 무조건 램에 영원히 본드로 붙여놔!"라고 시스템 콜 호출. │
│ - 결과: PFF 알고리즘의 대상에서 제외됨. 램은 낭비되지만, │
│ 언제 찔러도 0.1ms의 응답 속도를 1년 내내 방어함! │
└────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] PFF와 워킹 셋 모델은 "모자란 자원을 1byte라도 아껴서 여러 명이 나눠 쓰자"는 1980년대 빈곤의 시대 철학이다. 현대의 거대 기업들은 "램 128GB 더 꽂아줄 테니, 내 돈 벌어다 주는 코어 앱은 절대 뺏기지 말고 100% 점유해라"라는 메모리 결박(Pinning) 아키텍처를 선호한다. PFF는 이제 백그라운드 잡동사니 프로그램들을 통제하는 용도로 전락했다.
- 📢 섹션 요약 비유: PFF는 렌터카 시스템입니다. 안 쓸 땐 반납해서 돈(메모리)을 아끼고, 필요할 때 다시 빌립니다. 하지만 구급차나 소방차(핵심 서버)를 렌터카로 쓰면 위급할 때 빌리러 가다(Page Fault) 사람이 죽습니다. 이런 놈들은 돈이 아무리 들어도 무조건 자가용으로 사서 주차장에 24시간 대기시켜야(mlock) 합니다.
Ⅴ. 기대효과 및 결론 (Future & Standard)
기대효과
PFF 메커니즘을 통해 운영체제는 복잡한 워킹 셋 시뮬레이션 없이도, **"각 프로세스가 최소한으로 숨을 쉴 수 있는 프레임 개수"**를 실시간으로 조율하여 스래싱을 방어하고, 다중 프로그래밍 정도(DOM)를 램이 허용하는 극한의 스위트 스팟(Sweet Spot)으로 자동 유지할 수 있다.
결론 및 미래 전망
페이지 부재 빈도(PFF)는 워킹 셋 이론의 비현실적 오버헤드를 타파하고 실제 상용 운영체제가 메모리의 팽창과 수축을 다룰 수 있게 해 준 가장 현실적이고 우아한 실용 알고리즘이다.
그러나 현대의 백엔드 인프라(K8s 등 컨테이너 환경)에서는 OS가 동적으로 메모리를 뺏었다 줬다 하는 행위 자체가 '예측 불가능한 응답 지연(Jitter)'을 낳는 원흉으로 지목받고 있다. 미래의 트렌드는 OS의 얌체 같은 PFF 자동 조절을 끄고, 대신 Cgroups를 이용해 Limit과 Request를 명시적으로 하드코딩하여 "내가 선언한 메모리는 끝까지 보장받고, 넘치면 차라리 죽겠다"는 결정론적(Deterministic) 자원 할당 패러다임으로 완전히 교체되고 있다.
- 📢 섹션 요약 비유: PFF는 수압이 변할 때마다 알아서 밸브를 조여주는 똑똑한 자동 샤워기였습니다. 하지만 한 번 씻을 때 갑자기 찬물이 나오는 찰나의 순간(Jitter)조차 견디지 못하는 현대인(클라우드 아키텍처)들은, 아예 수조 통 자체를 개인 전용으로 따로 파버려서(Cgroups Isolation) 누가 물을 쓰든 내 샤워기 수압은 평생 변하지 않게 만드는 방향으로 진화했습니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| 워킹 셋 (Working Set) | PFF의 아버지. 철학은 완벽했으나 CPU를 너무 갉아먹어서 PFF라는 현실 타협안을 낳게 한 이론적 롤모델이다. |
| 스래싱 (Thrashing) | PFF 상한선(Upper Bound)이 계속 뚫리는데 남는 램이 없을 때 터지는, PFF가 가장 두려워하는 최악의 시스템 정지 사태다. |
| 페이지 폴트 (Page Fault) | PFF가 유일하게 쳐다보는 지표(Metric). 이 인터럽트가 몇 번 터졌는지만 세어보고 예산을 깎거나 늘린다. |
| 다중 프로그래밍 정도 (DOM) | PFF 하한선(Lower Bound)이 터져서 잉여 램을 왕창 회수했을 때, OS가 신나서 이 DOM을 올려 CPU를 혹사시킨다. |
| 요구 페이징 (Demand Paging) | 처음엔 램을 0개만 주고, 폴트가 터질 때마다(PFF 상한 돌파) 디스크에서 퍼 나르는 가장 게으른 기반 기술이다. |
👶 어린이를 위한 3줄 비유 설명
- 뷔페에서 엄마가 "그릇에 음식을 꽉 채워와서 버리지 마!"라고 하셨어요.
- 내가 음식을 너무 조금 가져와서 1분에 3번씩 자꾸 뷔페로 달려가면(페이지 폴트 상한선), 엄마가 "그릇이 너무 작네! 더 큰 그릇(프레임 추가)을 줄게!" 하세요.
- 반대로 내가 음식을 잔뜩 가져와 놓고 한 시간 동안 뷔페에 가지 않으면, 엄마가 "너 그릇이 너무 커서 낭비네! 작은 그릇으로 바꿔!" 하고 뺏어버리는 눈치 빠른 조절법이 PFF랍니다!