Backend

[JAVA]java.lang.OutOfMemoryError: GC overhead limit exceeded 예방법 (java.io 의 close 필요성)

Dddu 2024. 2. 26. 01:42

대고객 서비스를 운영중,  어느날 갑자기 서비스 접속이 되지 않는 장애를 겪었다. 

WAS 서버 두 대가 다 죽어있고 로그를 받아 확인해보니 이런 에러가 있었다.

java.lang.OutOfMemoryError : GC overhead limit exceeded 

 

위 에러를 처음 접하게 되어 많은 구글링을 하며 얻었던 정보를 정리해보고자 한다.


OutOfMemory란 ?

메모리 누수가 발생 or 시스템 용량 부족 JVM이 새로운 객체를 힙 메모리에 할당할 수 없을 때 발생한다.

이미 힙 메모리가 가득 차 있고, 가비지 컬렉터가 회수할 힙 메모리를 찾지도 못하는 상황.

 

GC 란 ?

GC (Garbage Collection)의 줄임말. 자바의 메모리 관리 방법 중 하나로 JVM의 힙 영역에서 동적으로 할당했던 메모리 영역 중 필요 없게 된 메모리 영역을 주기적으로 삭제하는 프로세스. 개발자가 메모리 관리를 크게 하지 않아도 어느정도 GC가 해준다는 장점이 있다. 

하지만 GC가 동작하는 동안에는 JVM의 다른 동작들을 잠깐 멈추기 때문에 오버헤드가 발생한다는 단점이 있다. 

 

그러면 GC overhead limit exceeded 란 ?

이 메시지는 어떤 이유로 가비지 콜렉터가 과도한 시간 (기본적으로 프로세스의 모든 CPU 시간의 98 %)을 소비하고 각 실행에서 매우 적은 메모리 (기본적으로 힙의 2 %)를 복구 함을 의미한다.


* 원인 및 해결방법

 

인프라 적인 원인은 아니고, 코드의 문제라 판단되었다.

 

JAVA.IO 부분을 잘 찾아보라는 팁을 받게 되어 집중적으로 찾아본 결과

BufferedWriter 및 HttpURLConnection로 인하여 OutfOfMemory가 발생했다.

 

BufferedWriter의 경우 버퍼를 잡아 놓았기 때문에 반드시 flush() 및 close()를 반드시 호출해 주어야 하는데 write/flush 후 close를 안 하는 부분도 있고 로직이 새는 부분이 있었다.

 

필요한 부분에 close를 넣고 try-catch-finally 구문에서 finally 부분에도 close()를 넣어 해결하였다.

Apache JMeter를 사용하여 부하테스트 시 문제 없음을 확인했다.

 

또, HttpURLConnection API document를 확인해보면 ." Releases this connection so that its resources may be either reused or closed" 이런 문구도 적혀있다.

 

java의 I/O 는 GC로도 해결이 안 되어 close() 메소드가 있는 것들은 모두 닫아 주는 습관을 들이는게 좋다고 한다.