OOM Killer — 메모리 부족 시 리눅스가 프로세스를 선택하는 방법
Linux OOM Killer의 동작 원리, oom_score·oom_score_adj 계산, dmesg 로그 분석, 중요 서비스 보호 방법, overcommit 설정, earlyoom 활용법을 설명합니다.
지난 글에서 페이지 캐시가 메모리를 관리하는 방식을 살펴봤습니다. 이번에는 메모리가 완전히 바닥날 때 리눅스가 취하는 극단적인 조치인 **OOM Killer(Out-Of-Memory Killer)**를 알아봅니다.
OOM이란
리눅스는 기본적으로 메모리 오버커밋을 허용합니다. malloc()으로 4GB를 할당해도 실제 접근 전까지는 물리 메모리를 사용하지 않습니다. 하지만 여러 프로세스가 실제로 대규모 메모리를 사용하기 시작하면 물리 RAM과 스왑이 모두 소진됩니다.
이 상황에서 커널은 새 메모리 할당 요청을 거부하거나 일부 프로세스를 강제 종료하는 선택을 해야 합니다. 후자를 담당하는 것이 OOM Killer입니다.
oom_score — 희생 대상 결정
OOM Killer는 각 프로세스에 oom_score (0~1000)를 부여합니다. 점수가 높을수록 먼저 종료됩니다.
# 특정 프로세스의 oom_score 확인
cat /proc/1234/oom_score
# 전체 프로세스 oom_score 순위
for p in /proc/[0-9]*/oom_score; do
awk -v f="$p" '{print $1, f}' "$p"
done | sort -rn | head -10
점수 계산 요소:
- 메모리 사용량이 많을수록 높은 점수
- 자식 프로세스 메모리도 포함
- root 프로세스는 약간 낮은 점수
- 오래 실행된 프로세스는 약간 낮은 점수
oom_score_adj — 수동 조정
/proc/PID/oom_score_adj에 -1000~1000 범위의 값을 쓰면 점수를 조정할 수 있습니다.
# 중요 서비스 보호 (낮은 점수)
echo -900 | sudo tee /proc/$(pgrep nginx)/oom_score_adj
# 절대 종료하지 않음 (-1000)
echo -1000 | sudo tee /proc/1/oom_score_adj
# 테스트/덜 중요한 프로세스 (높은 점수)
echo 500 | sudo tee /proc/$TEST_PID/oom_score_adj
systemd 서비스에서 영구 설정:
# /etc/systemd/system/myservice.service
[Service]
OOMScoreAdjust=-900
OOM 발생 감지
# 커널 로그에서 OOM 확인
sudo dmesg -T | grep -i 'oom\|killed' | tail -20
# systemd 저널 (더 보기 좋음)
sudo journalctl -k | grep -i 'out of memory\|killed process'
# 실시간 모니터링
sudo dmesg --follow | grep -i oom
OOM 로그 예시:
[Thu May 26 10:12:34 2026] Out of memory: Kill process 12345 (java) score 450 or sacrifice child
[Thu May 26 10:12:34 2026] Killed process 12345 (java) total-vm:2048000kB, anon-rss:1024000kB, file-rss:0kB
total-vm은 가상 메모리 크기, anon-rss는 실제 점유한 익명 페이지입니다.
메모리 오버커밋 설정
# 현재 오버커밋 모드 확인
cat /proc/sys/vm/overcommit_memory
# 0 (기본): 경험적 오버커밋 허용 (대부분의 malloc 허용)
# 1: 무조건 허용 (malloc이 절대 실패 안 함)
# 2: 제한 모드 (물리 RAM + swap의 N%만 허용)
# 오버커밋 비율 설정 (모드 2에서)
cat /proc/sys/vm/overcommit_ratio # 기본 50 (50%)
Redis는 vm.overcommit_memory=1을 권장합니다. 백그라운드 저장(RDB)이 fork() 시 CoW로 메모리를 많이 쓰기 때문입니다.
# Redis 권장 설정
sudo sysctl vm.overcommit_memory=1
cgroup으로 메모리 한도
OOM Killer보다 예방적인 방법입니다. cgroup v2로 프로세스 그룹의 메모리를 제한합니다.
# systemd 서비스에서 메모리 한도
# /etc/systemd/system/myapp.service
# [Service]
# MemoryMax=512M
# MemoryHigh=400M # 소프트 한도 (회수 촉진)
# 컨테이너 (Docker)
docker run --memory=512m --memory-swap=512m myapp
# cgroup v2 직접 설정
echo 536870912 > /sys/fs/cgroup/myapp/memory.max
한도 초과 시 해당 cgroup 내에서만 OOM Killer가 작동하여 다른 서비스에 영향을 주지 않습니다.
earlyoom — 더 스마트한 종료
커널 OOM Killer는 시스템이 이미 메모리 스래싱 상태에 빠진 후에야 발동합니다. earlyoom은 메모리 사용률이 임계값에 도달하면 미리 프로세스를 종료합니다.
# 설치
sudo apt install earlyoom
# 설정: 메모리 5% 이하, 스왑 5% 이하일 때 작동
# /etc/default/earlyoom
# EARLYOOM_ARGS="-m 5 -s 5 --avoid '(^|/) (init|sshd|systemd)'
sudo systemctl enable --now earlyoom
earlyoom은 SIGTERM을 먼저 보내 정상 종료할 기회를 주고, 응답 없으면 SIGKILL을 보냅니다. 커널 OOM Killer는 바로 SIGKILL을 씁니다.
트러블슈팅 시나리오
서버에서 프로세스가 갑자기 죽었을 때:
# 1. OOM 여부 확인
sudo dmesg -T | grep -E 'oom|killed' | tail -20
# 2. 어떤 프로세스가 많은 메모리를 쓰는지
ps aux --sort=-%mem | head -15
# 3. 현재 메모리 상태
free -h && cat /proc/meminfo | grep -E 'Cached|Dirty|Writeback|MemAvailable'
# 4. oom_score 높은 프로세스 목록
for p in /proc/[0-9]*/oom_score; do
awk -v f="$p" '{if ($1 > 100) print $1, f}' "$p" 2>/dev/null
done | sort -rn | head -10
지난 글: Page Cache — 리눅스 메모리 캐시의 핵심 구조
다음 글: Container Namespaces 상세 — 컨테이너 격리의 실제 구현
읽어주셔서 감사합니다. 😊