MAT(Memory Analyzer Tool) 로 OOM 분석
문제 발생
320만 건(약 2.5GiB)의 데이터를 8GB 서버에서 처리 중 OOM 발생
원인 파악
1. jconsole로 확인했을 때 Heap 메모리는 충분했음
used: 현재 JVM이 사용 중인 Heap 메모리 (약 654MB)
committed: JVM이 OS로부터 확보한 Heap 메모리 (약 1GB)
max: JVM이 사용할 수 있는 최대 Heap 크기 (약 5GB)
2. jstat으로 GC 모니터링 → Survivor 영역(S0, S1)의 사용률이 비정상적(한쪽은 0%, 한쪽은 100%)
jstat 으로 2초 마다 GC 정보 확인
- 데이터가 어느정도 로드된 후에는 GCT 가 점점 늘어난다.
- S0 , S1 은 Survivor 영역인데 S0은 0, S1은 계속 100프로로 고정되어 나온다.
* Minor GC가 발생할 때 Eden 에 있는 obj 들이 S0 혹은 S1 로 이동하고,
* Minor GC가 발생하면 S0 -> S1 혹은 S1 -> S0 으로 데이터가 옮겨지며 obj 의 age가 늘어나고, 한쪽은 완전히 비워진다.
* Meta 도 꽤 높이 차지하고 있다. 98-9% 였다가 MaxDirect 를 늘려주니까 96으로 아주 조금 떨어진 것 같다.
3. top 명령어로 시스템 메모리 사용량 확인
- 메모리 사용량 확인 : used 3,225,240 free 1,305,580
4. Heap Dump 생성 후 MAT(Memory Analyzer Tool) 로 분석
OOM 을 heapDump 로 떠서 MAT 로 분석.
결론 : ResultSet 쪽에서 발생하는 오류 , ResultSet이 1.9GB의 메모리를 점유
- 문제 코드
ResultSet rs = loadStmt.executeQuery(loadQuery);
MAT 화면
details >> 를 누르면
여기서 object 를 클릭하면 List ojbects > outgoing references 를 누르면 아래와 같은 list_objects 를 볼 수 있다.
retained Heap 에 2,115,608,792 를 1,024로 세 번 나누면 1.9 GB 이다.
Xmx를 1gb 늘려보고 Xms 를 Xmx 와 동일하게 늘렸다.
그리고 gc발생 시 이그나이트에서 connection time out 이 발생해 timeout 설정 시간을 늘렸더니 해결됨.
문제 원인
- JDBC ResultSet을 한꺼번에 메모리에 올려놓고 사용하여 메모리 부족 현상 발생
-> JDBC ResultSet을 한꺼번에 메모리에 올려놓으면, Heap이 아닌 Native Memory(Direct Buffer, Metaspace 등) 를 과다 사용하여 OOM이 발생할 수 있음 - GC 발생 후 Ignite에서 Connection Timeout이 발생하는 문제도 함께 확인됨
- Meta 영역 사용량도 높은 상태
해결 과정
- Xmx(최대 Heap) 값을 1GB 늘리고, Xms(초기 Heap)도 Xmx와 동일하게 설정
- 즉, -Xms와 -Xmx를 같은 값으로 설정해 Heap 크기를 고정하여 GC 오버헤드를 줄임
- Ignite의 Connection Timeout을 늘려서 GC 중에도 안정적인 연결 유지
결론
- 결국 ResultSet을 과도하게 메모리에 로드하는 것이 문제였고, Heap 크기 조정 + Timeout 설정 변경으로 해결됨