Java Spring Boot 3.x 메모리 누수(Memory Leak) 진단: Heap Dump 분석과 Eclipse MAT 활용법
🎯 타겟 독자: OutOfMemoryError(OOM)로 서버가 주기적으로 셧다운 되는 현상을 겪는 백엔드 개발자, 힙 덤프(Heap Dump) 분석이 처음인 엔지니어.
📝 요약: Spring Boot 애플리케이션에서 메모리 누수가 발생했을 때, 단순히 힙 메모리(Xmx)를 늘리는 것은 임시방편입니다. 이 글에서는 VisualVM과 Eclipse MAT를 사용하여 실행 중인 JVM의 덤프를 뜨고, 메모리를 점유하고 있는 범인(Dominator Tree)을 색출하여 근본적으로 해결하는 과정을 다룹니다.
1. 문제 상황: "서버가 밤마다 죽어요"
트래픽이 적은 시간대임에도 불구하고 Java 애플리케이션이 java.lang.OutOfMemoryError: Java heap space 에러를 뱉으며 비정상 종료된다면, 이는 전형적인 메모리 누수(Memory Leak)의 신호입니다.
가비지 컬렉터(GC)가 더 이상 참조되지 않는 객체를 청소해야 하는데, 코드 어딘가에서 불필요한 객체를 계속 붙잡고(Reference) 있어서 GC가 수거하지 못하는 상황입니다. 이는 물통에 구멍이 난 것이 아니라, 물이 빠져나갈 배수구가 막힌 상황과 같습니다.
2. 진단 도구 준비
로그만 봐서는 어떤 객체가 메모리를 차지하는지 알 수 없습니다. 다음 두 가지 도구가 필요합니다.
- VisualVM: JDK에 포함된 무료 도구로, 실시간 CPU/메모리 사용량을 모니터링하고 덤프 파일을 생성할 수 있습니다.
- Eclipse MAT (Memory Analyzer Tool): 생성된 덤프 파일(.hprof)을 분석하여 보기 좋게 시각화해 주는 강력한 분석 툴입니다.
3. 분석 단계 (Step-by-Step)
Step 1. 힙 덤프(Heap Dump) 생성
서버가 죽기 직전의 상태를 스냅샷으로 남겨야 합니다. 운영 중인 서버라면 OOM 발생 시 자동으로 덤프를 뜨도록 시작 옵션을 추가하는 것이 좋습니다.
Step 2. Eclipse MAT로 'Dominator Tree' 분석
생성된 .hprof 파일을 MAT로 열어 'Dominator Tree' 기능을 실행합니다. 이 기능은 가장 많은 메모리를 점유하고 있는 객체를 상위 순서대로 보여줍니다.
대부분의 경우, 상위 1~3개 객체가 전체 힙 메모리의 50% 이상을 차지하고 있다면 그곳이 범인입니다. 특히 Retained Heap(해당 객체가 사라지면 확보될 메모리 양)이 비정상적으로 큰 객체를 찾으세요.
4. 흔한 원인 Top 3 및 해결책
| 원인 유형 | 설명 | 해결 방법 |
|---|---|---|
| Static Collection | static List/Map에 데이터를 계속 담고 삭제하지 않음 |
static 사용 자제, LRU 캐시 사용 |
| Unclosed Stream | DB Connection, IO Stream을 close() 안 함 |
try-with-resources 구문 사용 |
| Thread Local | 요청 처리 후 스레드 변수 초기화 안 함 (Tomcat) | Interceptor의 afterCompletion에서 제거 |
🚨 잘못된 코드 예시 (Static 사용)
5. 결론
메모리 누수는 '천천히 다가오는 재앙'입니다. 배포 초기에는 문제가 없다가 데이터가 쌓이는 일주일 뒤에 터질 수 있습니다.
Spring Boot 개발 시에는 static 변수 사용을 극도로 자제하고, 외부 리소스를 사용할 때는 반드시 자원을 반납하는 습관을 들여야 합니다. OOM 발생 시 당황하지 말고 힙 덤프를 확보하는 것이 문제 해결의 90%입니다.