Out Of Memory 문제 분석
서비스를 개발 하거나 운영하면서 OOM(Out Of Memory)을 만나는 경우가 있다.
이런 경우 단편적 해결 방법으로는 서비스를 재기동하는 방법이 있겠다.
하지만 우리는 Out Of Memory가 왜 발생했는지 알고 원인을 해결헤야 하는데,
이 과정에서 heap dump를 분석할 필요가 있다.
heap dump란?
우선 heap에 대해 알아야하는데, heap은 JVM에서 관리하는 메모리 공간으로 Object들이 저장되는 공간이다.
primitive type의 경우 stack area에 저장되나 객체의 경우는 stack에는 참조를 저장하고 실제 Object는 heap에 저장되게 된다.
이러한 부분을 이해한 상태에서 대량의 객체가 생성된다면, 그 객체는 heap에 생성이 된다.
하지만 heap은 한정된 자원으로 계속해서 객체가 생기면 더 이상 생성할 공간이 부족해지게 되고, OOM(Out Of Memory) Error가 발생하게 된다.
이때, heap의 상태를 snapshot으로 저장된 것이 heap dump파일이다.
Heap Dump 테스트
Heapdump 테스트를 위해 Intellij의 JVM heap을 임의로 설정 후 객체를 무한정 생성해 본다
1. 프로젝트의 Run/Debug Configuration 에서 JVM Option으로 최소, 최대 힙사이즈를 1Gb로 부여하고 heap dump를 생성하는 옵션과 heap dump path를 지정해준다
* OOM 발생 시 heap dump 파일을 생성하기 위해서는 "-XX:+HeapDumpOnOutOfMemoryError" JVM 옵션을 꼭 지정해줘야 한다
2. 테스트 코드 작성
import java.util.ArrayList;
import java.util.List;
public class OomTest {
public static void main(String[] args) {
List<DummyDto> dummyDtoList = new ArrayList<>();
long num = 0;
while (true) {
num++;
dummyDtoList.add(new DummyDto("더미에요 " + num));
}
}
static class DummyDto {
private String text;
public DummyDto(String text) {
this.text = text;
}
}
}
3. 위 코드를 수행해보면 아래처럼 OOM이 발생하고 프로그램이 종료된다
그리고 힘 덤프 파일을 log 디렉토리에서 **.hprof로 얻을 수 있다
Heap Dump 분석
heap dump를 분석기 위해서는 여러 분석 프로그램을 이용할 수 있는데,
MAT을 사용하여 분석하는 방법을 다룬다
MAT (Memory Analzer)
MAT을 다운 받고 heapdump 파일을 열면 Overview를 볼 수 있다
Overview에서 Leak Suspects로 이동하면 대략적인 OOM의 원인을 알 수 있다
이 건은 테스트 이기 때문에 전체 용량의 대부분을 Object[] 가 차지하는 것을 알 수 있지만 실제 운영 중인 시스템에서는 그 비율 다를 수 있다
Leak Suspects로만 정확하게 알 수 없을 수 있다
그런 경우 다른 정보를 참고하여 문제를 일으키는 친구를 찾아내어야한다
아래는 dominator tree인데 해당 데이터를 보면 Retained Heap를 보면 많이 차지하고 있는 포인트를 유추할 수 있고 해당 포인트의 계층구조를 타고 들어가면 우리가 찾던 문제의 원인이 되는 객체를 발견할 수 있고 해당 객체의 어떤 값이 들어있는지도 확인할 수 있다
추가로 stacktrace를 확인하면 어떤 로직을 수행하는 도중에 문제가 발생했는지 확인할 수 있다
원인을 찾았으니 해당 객체를 발생시키는 부분의 코드를 수정해주면 된다!
OOM을 발생시키는 원인
- JVM 메모리 할당 문제
- 많은 수의 동시 요청
- 비효율적인 데이터 조회
보통 데이터 조회 시 조건을 잘못지정하거나 페이징을 적용하지 않는 등 데이터를 과하게 가져오게 되면서 발생하는 경우가 대부분이기 때문에 개발할 때 이 부분을 신경쓰는 것이 중요하다