파편화 관리 및 조각 모음 (Memory Compaction)
핵심 인사이트 (3줄 요약)
- 본질: 리눅스 메모리 컴팩션(Memory Compaction)은 장시간 가동된 서버의 물리 램(RAM)이 극심한 외부 단편화로 조각났을 때, 커널이 백그라운드에서 사용 중인 페이지들을 메모리 앞쪽으로 이동(Migration)시키고 빈 공간을 뒤쪽으로 몰아 거대한 연속 프레임을 창출해 내는 물리적 셔플 연산이다.
- 가치: 페이징 시스템 하에서도 디바이스 드라이버나 Huge Page 등은 '물리적으로 끊기지 않은 거대한 램 공간'을 요구하는데, 이 조각 모음 없이는 램 총량이 남아돌아도
kmalloc이나 2MB 거대 페이지 할당이 모조리 실패(OOM)하는 굴레를 구원해 준다.- 융합: 가상 메모리 매핑이 실시간으로 변해야 하므로 요구 페이징(Demand Paging)의 페이지 테이블 업데이트 로직과 결합되어 동작하며, 과거 고전적 압축(Compaction)이 유발하던 극악의 시스템 멈춤(STW)을 막기 위해 디스크 스왑을 최소화하는 스마트한 마이그레이션 아키텍처로 진화했다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념: 메모리 컴팩션(Compaction)은 물리 메모리의 파편화(Fragmentation)를 해소하는 커널 내부의 청소 메커니즘이다. 흩어진 빈 프레임(Free Frame)을 하나로 모으기 위해, 데이터가 든 프레임을 빈 프레임 쪽으로 복사(Copy)하고 기존 자리를 반환하여 커다란 '연속된 텅 빈 공간'을 확보한다. (윈도우의 '디스크 조각 모음'과 원리가 완벽히 같다.)
-
필요성: "페이징을 쓰면 외부 단편화가 0%라며? 왜 조각 모음을 해?"라는 의문이 들 수 있다. 유저 프로그램은 4KB 단위로 찢어져도 MMU가 이어주니 상관없지만, 운영체제 커널의 디바이스 드라이버(DMA)나 초고속 Huge Page(2MB, 1GB)는 반드시 물리 램 자체가 2MB씩 쫙 연속으로 이어져 있어야만 한다. 서버가 1년 동안 켜져 있으면 램이 4KB 단위로 걸레짝처럼 찢어져 연속된 2MB를 찾을 수 없게 된다. 100GB 램이 남아도 2MB 연속 할당이 실패해 커널이 뻗는 사태를 막으려면, 흩어진 4KB들을 한쪽으로 싹 치우는 빗자루질(컴팩션)이 절실했다.
-
💡 비유: 메모리 컴팩션은 극장의 좌석 정리와 같다. 손님 100명이 영화를 보다 중간중간 30명이 나갔다(파편화). 전체 빈자리는 30석이지만 전부 1칸씩 떨어져 있다. 이때 10명 단체 관람객(Huge Page나 DMA 장치)이 와서 "우린 무조건 10자리 연속된 곳에 앉아야 해!"라고 억지를 부리면, 극장 직원이 기존 손님들에게 "죄송하지만 전부 왼쪽으로 한 칸씩 바짝 당겨 앉아주세요!(Migration)"라고 부탁하여 오른쪽에 거대한 10칸짜리 빈 공간을 창출해 내는 과정이다.
-
등장 배경 및 리눅스의 고뇌:
- 초기 스왑(Swap)의 재앙: 예전 리눅스는 큰 연속 공간이 필요하면 램에 있는 걸 냅다 하드디스크로 스왑 아웃(Swap-out) 시켜버렸다. 이 때문에 시스템이 수십 초씩 멈춰버리는 끔찍한 렉이 발생했다.
- 디스크 I/O 회피: "제발 램 안에서만 좀 해결해 보자!" 디스크로 쫓아내지 않고 램 안에서 빈자리로 데이터만 살짝 옮기는(Migration) 로직 구상.
- Memory Compaction (2.6.35 커널 도입): 리눅스의 천재 개발자 Mel Gorman이 스왑 없이 램 내부에서만 데이터를 압축 이동시키는 고도화된 마이그레이션 스캐너 아키텍처를 커널에 병합(Merge)하며 파편화 문제의 숨통을 텄다.
┌─────────────────────────────────────────────────────────────────────────┐
│ 리눅스 메모리 컴팩션의 시각적 동작 원리 (Two-Finger Scan) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ [ 컴팩션 전: 걸레짝이 된 물리 램 프레임들 ] │
│ (왼쪽 끝) (오른쪽 끝) │
│ [ █ ][ ][ █ ][ █ ][ ][ █ ][ ][ █ ][ █ ][ ] │
│ ※ █: 데이터 있음, [ ]: 4KB 빈 방. │
│ ⚠ 2MB 거대 페이지를 만들고 싶은데 연속된 빈방이 없음! │
│ │
│ ↓↓ 컴팩션 발동 ↓↓ │
│ │
│ 1. 왼쪽 스캐너 ─▶ 이동시킬 '데이터(█)'를 왼쪽에서부터 찾음 │
│ 2. 오른쪽 스캐너 ◀─ 데이터를 욱여넣을 '빈 방'을 오른쪽 끝에서부터 찾음 │
│ 3. 복사(Copy): 왼쪽의 █ 를 오른쪽 끝 빈방으로 통째로 복사하고 이동! │
│ 4. 매핑 갱신: 프로세스 페이지 테이블을 새 주소로 재빨리 수정(TLB Flush) │
│ │
│ [ 컴팩션 후: 깨끗한 연속 구역 확보 ] │
│ [ ][ ][ ][ ][ ][ █ ][ █ ][ █ ][ █ ][ █ ] │
│ └─ 20KB 연속된 텅 빈 공간(Big Hole) 탄생! ──┘ │
└─────────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 리눅스 컴팩션의 핵심은 '양방향 스캐너(Two-Finger Scan)'다. 왼쪽에서는 마이그레이션(이사) 시킬 블록을 찾고, 오른쪽에서는 이사 갈 빈집을 찾는다. 둘이 중간에서 만날 때까지 데이터를 계속 오른쪽 끝으로 복사해 던져버리면, 기적처럼 메모리 왼쪽 절반에 거대하고 깨끗한 텅 빈 활주로(연속 공간)가 뚫린다. 버디 시스템(Buddy System)은 이 거대한 공간을 덥석 집어 1MB, 2MB짜리 굵직한 블록으로 쾌재를 부르며 묶어버린다.
- 📢 섹션 요약 비유: 스마트폰 바탕화면에 앱 아이콘과 빈칸이 더럽게 섞여 있을 때, 아이콘들을 손가락으로 꾹 눌러 폴더(오른쪽)에 다 쑤셔 박아버리면, 바탕화면 메인 창(왼쪽)에 거대한 빈 공간이 생겨 커다란 위젯(Huge Page)을 달 수 있게 되는 쾌감입니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
어떤 메모리가 마이그레이션(이사) 가능한가?
모든 램 조각을 다 옮길 수 있는 것은 절대 아니다. 컴팩션 아키텍처의 가장 큰 벽은 "움직일 수 없는 녀석들(Unmovable Pages)"을 가려내는 것이다.
- 이동 가능 (Movable Pages):
- 일반 유저 앱(크롬, 워드 등)이 쓰는 가상 메모리 매핑 페이지들.
- 파일 캐시(Page Cache).
- 이들은 램 위치가 바뀌어도 페이지 테이블(PT) 화살표만 슬쩍 바꿔주면 CPU가 알아서 따라가므로 완벽하게 이사 가능하다.
- 이동 불가 (Unmovable Pages):
- 커널 데이터 구조체(슬랩 등): 페이지 테이블의 마법을 안 거치고 하드웨어가 직접 주소를 물고 있는 경우가 많아 옮기면 커널이 패닉(Crash)에 빠진다.
- DMA 버퍼: 네트워크 카드가 이 주소로 데이터를 쏘고 있는데 몰래 주소를 옮기면 데이터가 허공에 증발한다.
- 📌 문제: 100만 평 땅에 1평짜리 Unmovable 조각 하나만 박혀 있어도, 그 땅은 영원히 연속된 100만 평으로 합쳐질 수 없다 (컴팩션 실패의 주원인).
마이그레이션 3단계 파이프라인
리눅스 커널이 물리 메모리 4KB를 이사시키는 숨 막히는 트랜잭션 과정이다.
┌───────────────────────────────────────────────────────────────────────────┐
│ 페이지 마이그레이션의 무거운 오버헤드 사이클 │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 격리 (Isolation) │
│ - 옮길 페이지와 새 빈집을 커널 관리 장부(LRU/Buddy)에서 잠시 뺌. │
│ - 이 페이지를 쓰고 있던 유저 앱의 접근을 잠시 멈춤(Lock 획득). │
│ │
│ 2. 복사 및 매핑 갱신 (Copy & Update) │
│ - 구 주소(A)의 4KB 데이터를 새 주소(B)로 Memcpy (물리적 부하 발생) │
│ - 유저 앱의 페이지 테이블 엔트리(PTE)를 찾아 주소 B로 화살표 수정! │
│ - 해당 주소가 캐싱된 모든 CPU 코어에 TLB Flush(Shootdown) 타격! │
│ │
│ 3. 반환 (Putback) │
│ - பழைய 구 주소(A)를 빈 방(Free Page) 장부로 던져 넣어 연속 공간 확보. │
│ - 유저 앱 락(Lock) 해제, 정상 동작 재개. │
└───────────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 단순히 데이터만 복사한다고 끝나는 게 아니다. 주소가 바뀌었으니 그 주소를 물고 있던 '페이지 테이블' 장부를 뜯어고쳐야 하고, 장부가 고쳐졌으니 CPU 코어들 안의 'TLB 캐시'를 모조리 강제로 날려버려야(Flush) 한다. 즉, 메모리 컴팩션이 백그라운드에서 너무 격렬하게 돌면 멀티코어 서버 전체가 캐시 미스와 락(Lock) 경합으로 덜덜 떨며 렉에 빠지는 치명상을 입는다.
- 📢 섹션 요약 비유: 이삿짐(데이터)만 새집으로 나르는 게 끝이 아니라, 동사무소 전입신고(페이지 테이블 갱신)도 해야 하고, 친구들과 우체국에 바뀐 주소(TLB Flush)를 다 알려줘야 하는 지독한 행정 처리가 수반되는 고통스러운 작업입니다.
Ⅲ. 융합 비교 및 다각도 분석
비교 1: 컴팩션(Compaction) vs 스와핑/리클레임(Reclaim)
둘 다 빈 공간을 확보하려는 몸부림이지만 접근 방식(램 내부냐 외부냐)이 다르다.
| 비교 항목 | 메모리 반환 (Reclaim / Swap-out) | 메모리 컴팩션 (Compaction) |
|---|---|---|
| 목적 | 램(RAM)의 총 용량 잔고 자체를 늘리기 위함 | 램 용량은 놔두고, 물리적으로 연속된 큰 덩어리를 뭉치기 위함 |
| 데이터 동선 | RAM ──▶ 하드디스크(Swap)로 내쫓음 | RAM (A번지) ──▶ RAM (B번지)로 램 내부에서 셔플링 |
| 속도(페널티) | 디스크 I/O 발생으로 최악의 시스템 정지(수 초) 발생 | 램 내부 복사라 상대적으로 양호함(수 밀리초 수준) |
| 발동 조건 | 램 사용률이 100%에 도달해 램이 터지기 직전 | 남은 램은 많은데 파편화가 심해 Huge Page 생성에 실패할 때 |
Kswapd와 Kcompactd 커널 데몬의 협주
리눅스 서버 뒤에는 조용히 일하는 두 명의 청소부 요정(데몬 스레드)이 있다.
- kswapd (용량 청소부): 메모리 총량이 쪼들리면 백그라운드에서 몰래 깨어나 안 쓰는 파일 캐시나 유저 앱 데이터를 슬슬 디스크로 버리며 램의 총잔고를 채운다.
- kcompactd (정리정돈 청소부):
kswapd가 잔고를 채워줬음에도 램이 너무 찢어져(파편화) 2MB짜리 덩어리를 못 만들면 얘가 깨어난다. CPU 코어 하나를 점유하고 램 안의 박스들을 왼쪽 오른쪽으로 미친 듯이 밀어대며 거대 구멍(Huge Hole)을 조각해 낸다. 이 둘의 적절한 백그라운드 활약 덕분에, 유저가 메모리를 요구했을 때 시스템이 얼어붙는 일(Direct Reclaim / Direct Compaction)을 미연에 방지할 수 있다.
┌──────────┬────────────┬────────────┬────────────────────────────┐
│ 트리거 상황│ 총 메모리 상태│ 파편화 상태 │ 출동하는 데몬 스레드│
├──────────┼────────────┼────────────┼────────────────────────────┤
│ 잔고 고갈 │ 부족 🚨 │ 상관없음 │ kswapd (버리기) │
│ 거대 할당 │ 여유 🟢 │ 걸레짝 🚨 │ kcompactd (밀기) │
└──────────┴────────────┴────────────┴────────────────────────────┘
[매트릭스 해설] 실무에서 서버의 Load Average가 갑자기 미친 듯이 치솟을 때, 많은 엔지니어가 top이나 vmstat을 보며 원인을 찾는다. CPU 연산이 아니라 %system, %iowait이 치솟는다면 이 두 청소부 데몬이 살기 위해 램을 뒤엎으며 멱살 캐리를 하고 있는 현장일 확률이 99%다.
- 📢 섹션 요약 비유: 방에 물건이 꽉 차서 발 디딜 틈이 없으면 헌옷수거함(디스크)에 내다 버리는 게
Reclaim이고, 물건 총량은 적은데 바닥에 널브러져 있어서 침대를 놓을 자리가 없을 때 물건들을 서랍(램 한쪽)으로 싹 밀어 넣는 게Compaction입니다.
Ⅳ. 실무 적용 및 기술사적 판단 (Strategy & Decision)
실무 시나리오: Transparent Huge Pages (THP)의 파국과 Defrag 튜닝
- 문제 상황: 데이터베이스(Redis, MongoDB) 서버가 잘 돌다가 며칠에 한 번씩 1~2초간 뚝뚝 끊기는 치명적인 렉(Latency Spikes)이 발생했다.
- 원인 분석:
- 범인은 리눅스의 THP (투명한 거대 페이지) 설정과 Direct Compaction의 환장할 콜라보다.
- 리눅스는 속도를 높여주려고 유저가 요구하지도 않았는데 억지로 4KB 페이지들을 뭉쳐 2MB Huge Page로 승격시키려 든다.
- 램이 파편화되어 2MB가 없으면? 리눅스 커널은 유저 앱(DB)의 동작을 잠시 멈추고 **강제로(Direct) 메모리 컴팩션(조각 모음)**을 냅다 돌려버린다. 이 압축하는 시간 동안 DB의 초당 10만 건 처리가 올스톱된다.
- 실무 튜닝 해법:
- 시스템 엔지니어의 바이블 튜닝이다.
/sys/kernel/mm/transparent_hugepage/defrag옵션 값을always에서 **madvise**나 **never**로 바꿔버린다. - 번역하자면: "파편화 심해서 Huge Page 못 만들겠으면 제발 앱 멈추고 조각 모음(defrag) 하지 마! 그냥 쿨하게 포기하고 일반 4KB 페이지로 던져줘서 렉이나 안 걸리게 해!"라는 뜻이다. 성능 변동성(Jitter)을 죽도록 싫어하는 백엔드 서버의 국룰 세팅이다.
- 시스템 엔지니어의 바이블 튜닝이다.
안티패턴: 영구적 단편화 핀(Pinning)
커널 모듈이나 서드파티 드라이버를 짤 때 메모리를 kmalloc으로 잔뜩 받아놓고 영원히 안 돌려주면, 이 조각들은 컴팩션 로직이 손댈 수 없는 '알박기 텐트(Unmovable)'가 된다. 이 알박기가 램 곳곳에 지뢰처럼 깔리면, 커널이 아무리 양쪽으로 스캐너를 돌려대도 조각들이 걸려서(Pinning) 대형 구멍을 만들 수 없게 되고 결국 서버가 OOM 패닉에 빠진다.
- 📢 섹션 요약 비유: 넓은 잔디밭(RAM)에 텐트를 치려는데, 누군가 뽑을 수 없는 무거운 철근(Unmovable Page)을 10미터 간격으로 드문드문 박아놓으면, 잔디밭 총 평수가 1만 평이라도 결국 커다란 축구장 하나를 지을 수 없게 되어 땅이 쓸모없어지는 악몽입니다.
Ⅴ. 기대효과 및 결론 (Future & Standard)
정량/정성 기대효과
| 구분 | 내용 |
|---|---|
| Huge Page 활용률 극대화 | 2MB, 1GB 거대 페이지 생성을 지원하여 TLB 미스를 억제하고 데이터베이스 및 가상 머신(KVM)의 성능을 수배 향상 |
| 디스크 I/O(스왑) 회피 | 램 내부에서의 데이터 마이그레이션만으로 큰 공간을 뚫어내어, 수십 초가 걸리는 디스크 스와핑의 재앙을 사전 차단 |
| 시스템 수명(Uptime) 연장 | 서버를 재부팅 하지 않고도 백그라운드에서 지속적으로 파편화를 치유하여, 수년간 끄지 않는 무중단(Zero-downtime) 서버 운영의 기반 제공 |
결론 및 미래 전망
파편화 관리 및 조각 모음 (Memory Compaction) 아키텍처는 가상 메모리의 우아한 추상화(페이징) 뒤에 숨겨진, 하드웨어(물리 램)의 진흙탕 같은 물리적 한계를 닦아내는 운영체제의 피땀 어린 노가다 현장이다. "연속 할당(세그멘테이션) 시절에나 있던 압축이 왜 현대 페이징 OS에 있나?"라는 철학적 질문에, 이 기술은 "하드웨어 장치(DMA)와 극한의 성능(Huge Page)을 위해 결국 물리적 연속성은 영원히 필요하기 때문"이라는 명쾌한 해답을 내놓았다. 앞으로 클라우드 스케일이 커질수록 램의 파편화 속도는 더 가팔라질 것이며, AI를 접목하여 앱이 멈추지 않는 가장 한가한 타이밍을 노려 은밀하게 메모리를 셔플하는 'Predictive Compaction' 기술로 더욱 은밀하게 진화할 것이다.
- 📢 섹션 요약 비유: 페이징이 "방 지저분해도 가상현실(VR) 고글 쓰면 깨끗하게 보여!"라는 사기극이었다면, 메모리 컴팩션은 그 사기극에 속지 않는 진짜 현실의 육체 노동자(드라이버)들을 위해 뒤에서 매일 밤 남몰래 빗자루질을 하는 어머니(커널)의 희생입니다.
📌 관련 개념 맵 (Knowledge Graph)
- 외부 단편화 (External Fragmentation) | 4KB 프레임 단위로 램이 점유되어, 물리적으로 연속된 거대한 공간이 멸종해 버리는 근본적 질병
- Huge Page (거대 페이지) | TLB 효율을 위해 2MB 등 통짜 메모리를 요구하는 튜닝 기법으로, 컴팩션 데몬이 땀 흘려 공간을 만들어주는 1순위 고객
- TLB Shootdown | 컴팩션이 페이지를 다른 주소로 옮긴 뒤, 모든 코어의 옛날 캐시를 날려버리느라 유발하는 치명적인 멀티코어 성능 병목
- kcompactd / kswapd | 램의 파편화를 밀어내는 조각 모음 데몬(compact)과 디스크로 데이터를 쫓아내는 용량 확보 데몬(swap)
- 버디 시스템 (Buddy System) | 컴팩션이 밀어내어 만들어준 빈 조각들을 빛의 속도로 병합(Coalescing)하여 거대 블록으로 부활시키는 하부 엔진
👶 어린이를 위한 3줄 비유 설명
- 메모리 컴팩션이 무엇인가요? 장난감 상자(메모리)에 블록들이 듬성듬성 어지럽혀 있어서 커다란 로봇을 뚱뚱한 모양 그대로 넣을 수 없을 때 하는 '초특급 정리 정돈'이에요.
- 어떻게 정리하나요? 상자 구석구석 흩어진 작은 블록들을 전부 한쪽 벽으로 바짝 밀어서 뭉쳐버리면, 반대쪽 벽에 로봇을 눕힐 수 있는 거대한 빈 공간이 짠! 하고 생겨나요.
- 불편한 점은 없나요? 블록들을 밀어서 이사시키는 동안에는 상자가 너무 흔들려서 다른 친구들이 장난감을 꺼내 놀 수 없고(시스템 렉) 잠깐 얌전히 기다려야 한답니다.