LabHub

CPU·메모리 누수 판별 · 메모리를 읽는다 · 이론

RSS·VSZ·PSS 가 각각 재는 것

LabHub 에서 이어서 보기

한 줄 요약

VSZ 는 약속, RSS 는 실제로 물린 물리 메모리, PSS 는 공유분을 나눠 가진 몫이다. 파드 여러 개의 RSS 를 더하면 실제보다 많이 나온다 — 그때 봐야 할 것이 PSS 다.

왜 이게 필요했나

"메모리 누수 같다" 는 신고의 절반은 누수가 아니다. 프로세스가 커진 이유는 여러 가지다.

넷은 대처가 다르다. 구분하지 않고 "메모리를 늘리자" 로 가면 며칠 뒤 같은 일이 반복된다.

어떻게 동작하나

한 프로세스의 실제 사용량은 smaps_rollup 한 줄이 가장 정확하다.

grep -E '^(Rss|Pss|Private_Dirty|Swap):' /proc/<PID>/smaps_rollup

| 항목 | 뜻 |
|---|---|
| Rss | 물리 메모리에 올라온 전체(공유 라이브러리 포함) |
| Pss | 공유분을 사용자 수로 나눈 몫 — 합계를 낼 때 쓸 유일한 값 |
| Private_Dirty | 이 프로세스만 쓰는, 파일로 되돌릴 수 없는 부분 — 누수를 볼 때 이것 |

누수 판별은 한 시점의 값이 아니라 기울기로 한다. 부하를 일정하게 준 상태에서 Private_Dirty 를 몇 분 간격으로 찍어 단조 증가하면 누수 쪽이고, 어느 선에서 평평해지면 캐시 쪽이다.

흔한 착각

VSZ 를 보고 놀라는 것. 64비트 JVM 이나 Go 런타임은 VSZ 로 수십 GB 를 잡는다. 주소 공간을 예약했을 뿐 물리 메모리가 아니다. VSZ 는 거의 항상 무시해도 된다.

RSS 를 더하는 것. 컨테이너 10개가 같은 libc 를 쓰면 그 페이지는 물리적으로 하나인데 RSS 에는 10번 잡힌다. 합계는 PSS 로 낸다.

free -h 의 free 가 작다고 걱정하는 것. 리눅스는 남는 메모리를 전부 페이지 캐시로 쓴다. 봐야 할 값은 available 이다.

실무에서 진짜 중요한 것

컨테이너는 호스트가 아니라 cgroup 한도로 죽는다.

cat /sys/fs/cgroup/memory.max        # 한도cat /sys/fs/cgroup/memory.current    # 지금cat /sys/fs/cgroup/memory.events     # oom_kill 횟수

memory.current 에는 페이지 캐시도 들어간다. 그래서 파일을 많이 읽는 프로세스는 실제 힙이 작아도 한도에 닿는다. memory.statanon(익명 메모리)과 file(캐시)을 나눠 봐야 진짜 원인이 보인다. OOM 킬은 커널이 하는 일이라 애플리케이션 로그에 아무것도 안 남는다 — memory.eventsoom_kill 이 늘었는지가 유일한 증거인 경우가 많다.