OOM 이 발생하면 일반적으로 두 가지를 경험한다.
1. java heap space
2. GC overhead limit exceeded
❓ 원인 분석
- java heap space
heap 영역의 공간이 부족해서 발생하는 오류(주로 코드 내 메모리 누수)
- GC overhead limit exceeded
1. GC가 자주 발생하여 오버헤드 발생
2. GC를 수행하는 시간의 98%를 소비했지만, Heap 메모리의 2% 미만으로 복구
3. 응용 프로그램이 모든 메모리를 소진했고, GC 소요 시간이 너무 길고, 반복적인 실패를 할 때
같은 OOM 이지만 살짝 다른 의미를 지닌 에러이다.
일반적으로 코드 단에서 메모리 누수를 개선하거나, JVM Heap 사이즈를 증가시켜 해결하는 방법이 많이 보였다.
[참조]
https://stackoverflow.com/questions/1393486/error-java-lang-outofmemoryerror-gc-overhead-limit-exceeded
https://shs2810.tistory.com/69
💡 많이 알려진 방법
코드 단에 메모리 누수가 혹시 있는지 확인
사이즈가 큰 객체가 있는지, 불필요한 참조 변수들이 많은지 등 점검을 해봤으나 문제될 것은 없었다.
JVM Heap 사이즈 증가하는 방법
가장 확실하고 해결 편한 방법이지만 선택지에서 제외했다.
메모리가 정말 부족하거나 낮게 할당됐다면 고려해보겠지만, 주어진 환경에서 최대한 개선을 해보고자 JVM 및 프로젝트 분석을 시작했다.
💡 원인 분석하기
우선 JVM(GC)에 대해 모니터링 할 필요가 있었다.
OOM이 발생한 프로세스에 JVM(GC)이 어떻게 구성되어 있는지 확인했다.
사용한 명령어는 jstat 이다.
jstat은 Java 애플리케이션의 가비지 컬렉션(GC) 활동을 모니터링하는데 사용한다.
jstat -gc PID
PID 확인 방법 : https://sanghoo.tistory.com/105
위 jstat 명령어을 통해 각 영역별 할당량과 사용량을 알아낼 수 있다.
영역당 의미는 아래와 같다.
- S0C: 첫 번째 서바이버 영역의 용량 (KB)
- S1C: 두 번째 서바이버 영역의 용량 (KB)
- S0U: 첫 번째 서바이버 영역의 사용된 메모리 (KB)
- S1U: 두 번째 서바이버 영역의 사용된 메모리 (KB)
- EC: 에덴 영역의 용량 (KB)
- EU: 에덴 영역의 사용된 메모리 (KB)
- OC: 올드 영역의 용량 (KB)
- OU: 올드 영역의 사용된 메모리 (KB)
- MC: 메타스페이스 영역의 용량 (KB)
- MU: 메타스페이스 영역의 사용된 메모리 (KB)
- CCSC: 클래스로더 영역의 용량 (KB)
- CCSU: 클래스로더 영역의 사용된 메모리 (KB)
- YGC: Young generation 가비지 컬렉션이 수행된 횟수
- YGCT: Young generation 가비지 컬렉션에 소요된 총 시간 (초)
- FGC: Full 가비지 컬렉션이 수행된 횟수
- FGCT: Full 가비지 컬렉션에 소요된 총 시간 (초)
- GCT: 가비지 컬렉션에 소요된 총 시간 (초)
jstat 정보 : https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jstat.html
나의 경우에는 OC, OU인 OLD 영역의 용량이 너무 많이 사용하고 있었다.
OLD 영역은 Minor GC를 수 차례 겪은 후에 살아남은 객체들이 이동하는 공간으로 오래동안 GC되지 않고 살아남는 객체들의 영역이다.
그럼 Spring Framework에서 OLD 영역에 적재될 객체들은 무엇이 있을까?
대표적으로 싱글톤(Singleton) 빈(객체)들이다.
무의미하게 싱글톤 빈을 등록하여 사용하고 있는지 전체적인 점검이 필요하다고 생각했다.
💡 결론
프로젝트의 bean들을 점검한 결과, Bean Scope 최적화가 많이 부족하다는 것을 인지했다.
Bean Scope 최적화를 통해 OLD 영역 용량을 확보하고, 추이를 지켜볼 예정이다.
빈 스코프 정보 : https://docs.spring.io/spring-framework/reference/core/beans/factory-scopes.html
'Spring' 카테고리의 다른 글
[Spring/Mybatis] 마이바티스 조회 시 NULL 반환 이슈(returnInstanceForEmptyRow) (0) | 2024.06.24 |
---|---|
[Test] ConnectionTimeout / ReadTimeout 테스트 하기 (0) | 2024.04.09 |
[SpringBoot] H2 데이터베이스 PostgreSQL 모드로 변경하기 (0) | 2024.03.13 |
[Springboot] 외부에서 로컬 서버 접속하기 (0) | 2024.03.12 |
Mybatis cannot change the executortype when there is an existing transaction (2) | 2024.03.07 |
댓글