
Docker 환경에서 Java 개발자를 괴롭히는 RSS 증발 사건 추적기
JVM 기반 서비스 운영 중 힙 메모리가 안정적임에도 컨테이너가 OOM으로 종료되는 미스터리한 현상을 분석합니다. Docker의 cgroup과 리눅스 커널의 OOM Killer 작동 방식을 통해 왜 JVM 옵션만으로 메모리를 통제할 수 없는지, 그리고 NMT의 한계는 무엇인지 10년 차 개발자의 실무 경험을 통해 전달합니다.
Docker 환경에서 Java 애플리케이션을 운영하며 원인 모를 컨테이너 재시작을 겪고 있다면 반드시 NMT를 활성화하여 힙 외부 영역을 점검해보길 권장합니다.
JVM 힙 메모리(-Xmx)와 Metaspace 등을 엄격히 제한했음에도 불구하고, Docker 컨테이너 환경에서 전체 프로세스 물리 메모리(RSS)가 지속적으로 우상향하며 80%를 초과하는 현상이 발생했습니다.
문제 원인을 파악하기 위해 Native Memory Tracking(NMT)을 활성화하여 JVM이 관리하는 영역을 조사하고, top 및 jcmd 명령어를 통해 NMT가 추적하지 못하는 외부 네이티브 메모리 영역의 격차를 확인하며 원인을 좁혀 나갔습니다.
NMT상으로는 9GB의 메모리 점유가 확인되었으나 실제 RSS는 14GB에 달하는 약 5GB의 미스터리한 메모리 사용량을 발견했으며, 단순 JVM 옵션 조정이나 버전 변경으로는 해결되지 않음을 확인했습니다.
Trade-off
NMT 활성화 시 약 5~10%의 성능 오버헤드가 발생하며, NMT만으로는 JVM 외부 라이브러리에서 발생하는 malloc 할당량을 추적할 수 없다는 기술적 한계가 존재합니다.
프로세스가 현재 사용 중인 실제 물리 메모리 양을 의미하며, 공유 라이브러리와 스택, 힙을 모두 포함하는 지표입니다.
프로세스 그룹의 리소스 사용량을 제한하고 격리하는 리눅스 커널 기능입니다.
JVM 내부에서 발생하는 네이티브 메모리 사용량을 추적하여 리포팅하는 JVM 내장 기능입니다.




