새해를 맞이하여 회사에서 나에게 서프라이즈를 준비해주었다.
그것은 바로 팀이동이였다..!! (사실 n번째라 놀라지 않음)
어찌되었든 새로운 환경에서 살아남기 위해 다시 치열하게 싸워보려고 한다.
팀이동을하면 가장 먼저하는게 무엇인가?
바로, 개발환경 세팅이다. 프로젝트를 clone하고 build를 진행했다.
위 이미지같이 GC overhead limit exceeded 원인으로 OutOfMemoryError가 발생하고 있다. 잉..? 이런 에러는 살면서 처음보았다.
빨간줄을 없애보자
Build 시, GC overhead limit exceeded를 해결하는 것은 Intellij 환경에서는 식은 죽먹기이다.
Intellij의 Setting > Build, Execution, Deployment > Compiler 로 진입을 해보면
Shard heap size라는 항목을 살펴볼 수 있다. default로 지정되어있는 700Mbytes를 임의로 늘려주기만 하면 된다.
이렇게 끝내면 나는 돌이다.
- 킹명주 -
문제를 쉽게 해결했는데 뭔가 찝찝해서 3일간 잠을 설쳤다. 그래서 GC Overhead limit exceeded 단어 하나하나씩 뜯어보며 왜 발생했는지 분석해보고자 한다.
[GC] Garbage Collection이란?
C/C++ 개발을 해본 경험이 있다면 free()라는 함수를 사용해보았을 것이다.
int* arr = (int*)malloc(sizeof(int)*5);
free(arr);
free는 할당한 메모리를 해제해주는 함수이다. 이러한 작업을 JAVA에서 하는 것을 본적이 있는가..?
당연히 없을 것이다. 왜냐하면 JVM의 Garbage Collector가 불필요한 메모리를 알아서 해제해주기 때문이다. 그렇다면 Garbage Collection은 어떤 객체를 Garbage로 판단할까?
Runtime Data Area룰 그려보았는데, 현재 heap 영역에서 아무것도 참조되지 않고 있는 인스턴스가 보일 것이다. 이를 Unreachable라고 부르는데, 이것이 바로 GC의 대상이 되는 것이다.
만약, GC를 딱 한 단어로 정의하라고 한다면 우리 ❤️어머니❤️ 라고 할 것이다. 어머니는 나의 책상이 더러워지는걸 못보고 2일 이상 놓여진 영수증, 책 등을 정리하신다. 역시 어머니는 어릴때부터 GC를 조기교육 해주셨다.
[overhead] 그래서 GC는 왜 overhead가 발생할까?
GC는 unreachable한 instance를 제거하기 위해 모든 작업을 중단시킨다. 그 후, GC 작업이 끝나면 다시 작업을 진행한다. 이를 STW(Stop The World)라고 부른다. 그림으로 쉽게 이해해보자.
Application Thread가 동작하고 있다가 GC Thread 수행되면서 중단된다. 해당 GC Thread에서는 unreachable instance를 제거하는 작업을 수행한다. GC 작업이 끝난 후, Application Thread가 다시 동작한다. 그렇기에 Stop the World가 빈번하게 발생하거나, 많은 시간이 걸린다면 오버헤드가 발생하게 될 것이다.
[exceed] 왜 limit exceeded가 발생할까?
오라클 공식문서를 참고해보면 JVM이 전체 실행 시간의 98% 이상을 Garbage Collection에 사용하거나 Garbage Collection 이후, 전체 힙 메모리가 2% 미만으로 확보되는 상태가 5번 지속된다면 때 overhead가 한계치를 초과했다고 판단한다.
정리해보자면..
최종적으로 우리 프로젝트에서 GC Overhead limit exceeded가 발생한 이유는 heap memory가 충분하게 확보되지 못했기 때문이다.
프로젝트의 Instance에 비해 heap memory가 부족하여 Garbage Collection이 빈번하게 발생했을 것이다. 그럼에도 전체 heap memory가 확보되지 않고 계속해서 overhead가 발생한 것이다.
heap size를 늘리는게 정답일까?
heap memory가 부족하니 엄청나게 늘려두면 10년은 고민하지 않아도 되겠지?!
라고 생각하면 큰일난다. GC는 Heap memory의 임계값에 도달하면 실행되는데, 엄청나게 늘려두면 한번에 많은 unreachable instance를 처리하게 될것이다.
위와 같이 Stop The World의 시간이 너무 길어지면 애플리케이션 성능에 영향을 미칠 수 있고, 전체 실행 시간의 98% 이상을 Garbage Collection에 사용하여 여전히 GC Overhead limit exceeded가 발생할 수 있다.
그러므로 프로젝트의 visualVM과 같은 툴을 사용하여 heap memory를 파악하고 적절한 수치로 변경하는 것이 좋다. 이와 관련된 내용은 다음 글에 좀 더 자세하게 다루겠다. 아마 다음 주제는 GC와 heap memory 심층적인 개념이해와 visualVM + visualGC를 활용하여 분석일 것같다.
결론
이번 글은 OutOfMemoryError 문제의 원인을 찾기 위한 여정을 서술해보았다.
요즘 단순업무를 많이하다보니 이렇게라도 challenge를 찾아나가야겠다.
'JAVA' 카테고리의 다른 글
[JAVA] Enum "equals" vs "==" (2) | 2024.08.21 |
---|---|
Java에서 Stack Class는 함정카드다. (4) | 2024.06.13 |
Java 8 Functional Interface에 대해 알아보자. (0) | 2023.10.31 |