JVM 옵션 잘못 건드렸다가 서비스 터진 경험
Java 서비스를 운영하다 보면 JVM 옵션을 조정해야 하는 순간이 옵니다. GC 튜닝이든, 메모리 제한이든 한 번쯤은 손을 대게 됩니다. 문제는 이 설정이 생각보다 민감하다는 점입니다.
특히 운영 환경에서는 작은 변경 하나가 전체 프로세스의 안정성에 직접적인 영향을 줍니다. 설정 몇 줄 바꿨을 뿐인데 서비스가 바로 죽는 상황도 충분히 발생할 수 있습니다.
문제 상황: 단순한 메모리 옵션 변경
당시 상황은 단순했습니다. 애플리케이션 메모리 사용량이 조금 높아 보였고, 이를 줄이기 위해 JVM 옵션을 조정하려고 했습니다.
기존에는 아래처럼 설정되어 있었습니다.
-Xms2g
-Xmx2g
여기서 메모리를 줄이기 위해 아래처럼 변경했습니다.
-Xms512m
-Xmx512m
겉으로 보면 단순히 메모리만 줄인 것처럼 보입니다. 실제로 많은 분들이 이 정도 변경은 가볍게 생각합니다.
증상: 서비스 기동 직후 바로 종료
배포 후 바로 문제가 발생했습니다. 애플리케이션이 정상적으로 올라오는 것처럼 보이다가 몇 초 뒤에 바로 종료되었습니다.
로그를 보면 OutOfMemoryError가 발생하고 있었습니다.
java.lang.OutOfMemoryError: Java heap space
애플리케이션이 처리 트래픽을 받기도 전에 죽어버리는 상황이었습니다. 초기화 단계에서 필요한 메모리조차 확보하지 못한 상태였습니다.
원인: 애플리케이션 요구 메모리보다 작은 설정
원인은 단순하지만 놓치기 쉬운 부분이었습니다. 애플리케이션이 기동될 때 필요한 최소 메모리보다 JVM 힙을 작게 잡은 것입니다.
Spring Boot 기반 서비스의 경우, 클래스 로딩, Bean 초기화, 캐시 생성 등 초기 단계에서 생각보다 많은 메모리를 사용합니다.
특히 다음과 같은 요소들이 영향을 줍니다.
- Spring 컨텍스트 초기화
- 라이브러리 로딩
- 캐시 또는 설정 데이터 로딩
- JIT 컴파일 초기 단계
이런 요소들을 고려하지 않고 단순히 숫자만 줄이면, 애플리케이션이 시작도 못 하고 종료되는 상황이 발생합니다.
헷갈리는 포인트: Xms와 Xmx의 관계
실무에서 자주 혼동하는 부분이 Xms와 Xmx입니다.
각 옵션의 의미
-Xms는 JVM이 시작할 때 할당하는 초기 힙 크기입니다. -Xmx는 JVM이 사용할 수 있는 최대 힙 크기입니다.
이 둘을 다르게 설정하면 JVM은 필요에 따라 힙을 확장하거나 축소합니다. 하지만 너무 작은 값으로 시작하면 초기 단계에서 바로 문제가 생길 수 있습니다.
실무에서의 선택 기준
일반적으로 운영 환경에서는 Xms와 Xmx를 동일하게 맞추는 경우가 많습니다.
이렇게 하면 힙 확장 과정이 없어지고, 예측 가능한 메모리 사용이 가능해집니다. 특히 컨테이너 환경에서는 이 방식이 더 안정적입니다.
대응 과정: 빠르게 원복 후 재조정
문제가 발생하자마자 가장 먼저 한 일은 설정을 원래대로 되돌리는 것이었습니다. 장애 상황에서는 원인 분석보다 서비스 복구가 우선입니다.
이후에는 아래 기준으로 다시 조정했습니다.
- 애플리케이션 기동 시 최소 필요 메모리 확인
- 로컬 환경에서 낮은 메모리로 테스트
- 점진적으로 줄이면서 안정성 확인
처음부터 목표 값을 바로 적용하기보다, 단계적으로 줄여가는 방식이 훨씬 안전합니다.
주의할 점: JVM 옵션은 코드보다 위험하다
코드는 리뷰라도 받지만, JVM 옵션은 종종 별도의 검증 없이 배포되는 경우가 있습니다. 이 부분이 의외로 리스크가 큽니다.
특히 아래 상황에서는 더 신중하게 접근하는 것이 좋습니다.
- 메모리 관련 옵션 변경
- GC 알고리즘 변경
- 컨테이너 환경에서 리소스 제한 변경
설정은 눈에 잘 보이지 않기 때문에, 문제가 생겼을 때 원인 파악이 더 어려워집니다.
정리
JVM 옵션은 단순한 설정값처럼 보이지만, 애플리케이션 동작에 직접적인 영향을 줍니다.
특히 메모리 관련 옵션은 아래 기준으로 접근하는 것이 좋습니다.
- 초기값을 무작정 줄이지 않는다
- 애플리케이션 기동에 필요한 최소 메모리를 먼저 파악한다
- 운영 적용 전 반드시 테스트한다
- 변경 시에는 점진적으로 조정한다
코드 수정 없이도 시스템을 망가뜨릴 수 있는 대표적인 영역이 JVM 옵션입니다. 설정을 바꿀 때는 항상 애플리케이션의 실행 흐름을 함께 고려하는 것이 중요합니다.
'개발 > JAVA' 카테고리의 다른 글
| [JAVA] Thread 수 증가로 인한 OutOfMemoryError 발생 원인 (0) | 2026.05.02 |
|---|---|
| [JAVA] Heap dump 분석해서 메모리 누수 잡은 방법 (0) | 2026.05.01 |
| [JAVA] G1GC로 바꿨는데 오히려 성능이 나빠진 이유 (0) | 2026.04.29 |
| [JAVA] Full GC 때문에 latency 튀는 문제 추적한 과정 (0) | 2026.04.28 |
| [JAVA] Metaspace OutOfMemoryError 해결하면서 알게 된 클래스 로딩 문제 (0) | 2026.04.27 |
