0
"스왑은 RAM의 2배" 규칙은 대체 어디서 온 걸까
backend
요약
기사 전체 정리
"스왑은 RAM의 2배로 잡아라" — 이 규칙은 대체 어디서 온 걸까?
- 이 규칙의 뿌리는 1990년대 SunOS 시절로 거슬러 올라감. 당시 SunOS는 메모리 오버커밋을 전혀 하지 않았음.
malloc이 성공하면 커널이 해당 바이트에 대해 스왑이든 RAM이든 반드시 제공하겠다고 "약속"하는 구조였음 - 여기서 "커밋(committed)"과 "사용(used)"의 구분이 핵심임. 커밋된 메모리는 커널이 약속한 양이고, 사용된 메모리는 프로세스가 실제로 쓴 양. 사용은 항상 커밋의 부분집합임
fork/exec가 만든 2배 공식
- 당시 워크로드는 실제 사용량 대비 약 50% 더 많은 메모리를 커밋하는 게 일반적이었음. 비싼 RAM을 아끼려면 "커밋은 됐지만 안 쓰는" 메모리를 스왑에 떠넘기는 게 합리적이었고, 그래서 스왑은 RAM의 약 50% 정도면 충분했음
- 근데 여기에
fork/exec가 등장함. 유닉스에서 새 프로세스를 띄우려면 먼저fork로 현재 프로세스를 통째로 복제한 다음exec로 새 프로그램을 올림. 문제는fork순간에 커널이 자식 프로세스에 대해서도 동일한 양의 메모리를 커밋해야 한다는 것 - copy-on-write 덕분에 실제로 쓰이진 않지만, "약속은 약속"이라 커널이 부모+자식 모두의 커밋을 감당할 수 있어야 했음. 즉 최악의 경우(fork 직후)를 대비하면 RAM + 스왑이 정상 상태 커밋 메모리의 2배여야 함
- 정상 상태 커밋 메모리가 RAM의 약 150%이므로, 총 메모리(RAM + 스왑)는 RAM의 3배가 필요함. 간단한 산술로 스왑 ≈ RAM × 2가 나옴
스왑 단편화라는 또 다른 이유
- HDD 시대에는 시크 타임이 있었기 때문에 스왑 영역에 연속된(contiguous) 빈 블록을 확보하는 게 중요했음. 스왑의 절반만 채워야 항상 연속 블록을 찾을 수 있다는 경험칙이 있었음
- 최대 부하 시 물리 RAM만큼의 데이터가 스왑에 올라간다고 가정하고, 연속 할당을 위해 스왑의 절반은 비워둬야 하니까 → 스왑 = RAM × 2. 세 가지 경험칙이 하나로 합쳐진 결과임
현대에는 의미 없는 규칙
- 현대 리눅스는 오버커밋을 지원함. 커널이 할당 시점에 약속하지 않고, 실제로 메모리가 부족해지면 OOM Killer를 소환하는 방식.
fork의 2배 문제 자체가 사라진 거임 - 당시에는 8MB RAM에 320MB HDD면 스왑 16MB가 디스크의 1/20에 불과했는데, 지금은 32GB RAM에 256GB SSD라면 스왑이 디스크의 1/4을 차지하게 됨. 비율 자체가 말이 안 됨
- JVM 같은 현대 런타임은 GC 돌 때 힙 전체를 주기적으로 훑기 때문에, 예전처럼 "커밋만 하고 안 쓰는 페이지"가 조용히 스왑에 누워있는 시나리오도 잘 안 맞음
참고
> 2배 규칙은 기술적 제약이 아니라, "잘 모르겠으면 일단 이렇게 하면 크게 안 망한다"는 경험칙이었음. 1.5배는 어중간하고 1배는 "이미 RAM이 있는데 왜?"라는 심리적 저항이 있어서, 소통하기 쉬운 2배가 살아남은 것
- 결론적으로 이 규칙은 SunOS의 노오버커밋 정책 + fork의 메모리 더블링 + HDD 스왑 단편화 회피라는 세 가지 시대적 조건이 만든 산물이고, 거기에 "2배는 외우기 쉽잖아"라는 인간 심리가 더해진 거임
댓글
댓글
댓글을 불러오는 중...