"어느 프로세스가 메모리를 먹는가" 를 ps 의 RSS 합으로 계산하면 실제 사용량보다 훨씬 크게 나온다. 공유 라이브러리를 여러 프로세스가 각자 자기 몫으로 세기 때문이다. 지표가 무엇을 세는지 알고 골라야 한다.
| 지표 | 뜻 |
|---|---|
| VSZ · VmSize | 가상 주소 공간 크기. 실제로 물리 메모리를 쓰고 있다는 뜻이 아니다 |
| RSS · VmRSS | 물리 메모리에 올라와 있는 양. 공유 페이지가 중복 계산된다 |
| PSS | 공유 페이지를 공유하는 프로세스 수로 나눠 배분한 값. 합계를 내기에 적합 |
| USS | 그 프로세스만 쓰는 양. 죽였을 때 실제로 반환되는 크기 |
# 메모리 사용률 상위 20
ps -eo pid,ppid,user,%mem,rss,vsz,comm --sort=-%mem | head -20
# 특정 프로세스의 상세 값
grep -E 'VmSize|VmRSS|VmSwap' /proc/1234/status
# 실시간 — top 에서 Shift+M 은 메모리 정렬
top
# 매핑별 상세. 마지막 줄이 합계
pmap -x 1234 | tail -1
# smaps_rollup 은 PSS 를 커널이 직접 합산해 준다 (커널 4.14+)
cat /proc/1234/smaps_rollup
sudo dnf install -y smem # EPEL
smem -k -s pss -r | head -20
smem -k -P nginx
smem 은 /proc/<pid>/smaps 를 읽어 계산하므로 프로세스가 많으면 느리다. 상시 감시용이 아니라 조사용으로 쓴다.
free -h 의 used 가 크다고 곧바로 문제가 아니다. available 을 본다. 페이지 캐시는 필요하면 즉시 반환된다.
free -h
cat /proc/meminfo | grep -E 'MemTotal|MemAvailable|Cached|Shmem'
Shmem 이 큰데 원인을 못 찾겠다면 /dev/shm 과 tmpfs 를 확인한다. 여기에 쌓인 파일은 메모리를 점유하지만 어느 프로세스에도 매달려 있지 않다.
df -h /dev/shm
du -sh /dev/shm/*
파일 디스크립터 고갈은 Too many open files 로 나타난다. 소켓도 디스크립터이므로 커넥션이 많은 서비스에서 먼저 터진다.
# 특정 프로세스가 연 개수
ls /proc/1234/fd | wc -l
# lsof 로 확인 — 헤더 한 줄이 포함되므로 그만큼 뺀다
lsof -p 1234 | wc -l
# 그 프로세스에 적용된 제한값
cat /proc/1234/limits | grep 'open files'
# 시스템 전체
cat /proc/sys/fs/file-nr
/proc/sys/fs/file-nr 의 세 값은 차례로 할당된 디스크립터 수, 사용하지 않는 수, 시스템 최대값이다.
ulimit -n 을 셸에서 올려도 이미 떠 있는 서비스에는 적용되지 않는다. systemd 로 띄운 서비스는 limits.conf 가 아니라 유닛의 LimitNOFILE 을 본다.
sudo systemctl edit myapp.service
[Service]
LimitNOFILE=65535
sudo systemctl daemon-reload
sudo systemctl restart myapp
cat /proc/$(pgrep -o myapp)/limits | grep 'open files'
어느 프로세스가 디스크립터를 붙들고 있는지 한눈에 보려면 다음을 쓴다.
for p in /proc/[0-9]*; do
printf '%s %s\n' "$(ls "$p/fd" 2>/dev/null | wc -l)" "$(cat "$p/comm" 2>/dev/null)"
done | sort -rn | head -20