개요
서버를 운영하다 보면 여러 이유로 힙 덤프를 분석할 일이 생깁니다. 힙 덤프 분석을 통해서 메모리 누수나 메모리 사용량이 높은 부분을 찾아내는데 도움이 됩니다.
처음 힙 덤프를 분석하려고 하면 어떻게 해야할지 막막할 수 있습니다. 그래서 그런 분들에게 도움이 되고자 힙 덤프 예제 문제와 함께 힙 덤프 분석 방법을 설명하겠습니다. 문제는 아래 링크에서 확인 할 수 있습니다. 문제 정답은 글 맨 아래에 있습니다.
https://github.com/marinesnow34/dump-example
문제
고라파덕은 멍때리며 코딩하는 것으로 유명하다. 어느 날, 고라파덕이 운영하는 서버에서 OOM(Out Of Memory) 오류가 발생했다. 다행히 Heap Dump 파일(dump.hprof)을 확보할 수 있었고, 이를 통해 문제를 분석할 수 있는 상황이다.
📌 분석할 내용
- 어떤 메서드에서 문제가 발생했는가?
- 어떤 상황(변수 값)에서 문제가 발생했는가?
소스 코드와 Heap Dump를 분석하여 고라파덕을 도와주자!!
정적 분석
본격적으로 힙 덤프를 분석하기 전에 간단하게 소스 코드를 확인 해 봅시다.
1 | // src/main/java/com/example/dump/domain/UserRepository.java |
일단 서비스를 확인해 보니 idx가 주어지면 idx를 가진 유저의 이름을 가져오고, 이름이 주어지면 이름을 가진 유저의 주소를 가져오는 것으로 보입니다.
코드로 봤을 때는 문제가 될만한 부분이 보이지 않습니다. 그럼 이제 힙 덤프를 분석해 봅시다.
힙 덤프 분석
힙 덤프를 분석하기 위해서는 MAT와
VisualVM을 사용했습니다. 먼저 MAT로 힙 덤프를 열어봅시다.

MAT에서 dump.hprof 파일을 열면 위와 같은 화면이 나옵니다. 먼저 Leak Suspects Report를 클릭해 줍니다. 분석되는 동안 Action > Dominator Tree를 합니다.

ArrayList의 capacity는 360,145이고 안에 들어있는 객체의 갯수는 349,584개 입니다. 이를 통해서 모종의 이유로 ArrayList가 너무 많은 객체를 가지고 있어서 OOM이 발생했을 것으로 추측할 수 있습니다.
아까 분석시킨 supects Report를 확인 합시다.
http-nio-8080-exec-2 스레드에서 문제가 발생했음을 알 수 있습니다. Detail을 확인하면 더 많은 정보를 얻을 수 있습니다. 가독성이 좋은 VisualVM으로 해당 스레드를 확인해 봅시다.

VisualVM에 메인에서 OutOfMemory Thread를 확인할 수 있습니다. 이번 상황에서는 메모리를 많이 들고 있는 쓰레드와 OOM이 발생한 쓰레드가 동일한 것을 알 수 있습니다. view all로 해당 쓰레드를 확인해 봅시다.

http-nio-8080-exec-2스레드의 스택트레이스가 보입니다. 길어서 저희가 호출한 부분을 찾아보겠습니다.
익숙한 UserService.getUserNameById()
메서드가 보입니다. findByIdx()
를 호출하다가 OOM이 발생했습니다. findByIdx()
를 확인해 봅시다.
local variable을 확인해 보면 long 값이 0인 것을 확인할 수 있습니다. 이는 idx가 0인 유저를 가져오는 것으로 추정됩니다. idx가 0인 유저는 36만명 이상으로 추측 됩니다.
정답 및 해설
- 어떤 메서드에서 문제가 발생했는가?
UserService.getUserNameById()
안userRepository.findByIdx(idx)
에서 idx가 동일한 유저를 모두 가져와 메모리 부족으로 인한 OOM이 발생했습니다.
- 어떤 상황(변수 값)에서 문제가 발생했는가?
- idx가 0인 유저를 가져올 때 문제가 발생했습니다. idx가 0인 유저는 36만명 이상으로 추측 됩니다.
고라파덕의 서비스는 어떤 이유로 idx가 0인 유저가 36만명 이상이 되었고, 이를 findByIdx(0L)으로 호출하게 됐습니다. NonUniqueResultException가 발생하기 전에 메모리가 부족해 OOM이 발생했습니다.