OutOfMemoryError: GC overhead

S_H_H·2023년 2월 24일
0
어느날 Heap Memory가 1초만에 가득 차버렸다
이건 또 무슨일인가

Error 발생

테스트 중 갑자기 무한로딩이 걸리더니 아무런 반응이 없어졌다.
로그를 봤지만 에러는 떨어지지 않았고 엄청 천천히 처리되고 있을때 메모리가 가득찬걸 확인할 수 있었다.

여러 사용자가 동시에 테스트를 진행하고 있다보니 누가 어떤 프로세스로 인해 원인을 파악하기 힘든 상태였다.

확실한건 힙 메모리가 순식간에 가득 찼다는건 용량이 큰 작업을 실행했다는 것

Error 파악

에러 내용을 보면 MemoryErrorGC가 가장 눈에 띄었다

java.lang.OutOfMemoryError: GC overhead limit exceeded

2개의 역할이 무엇인지 부터 파악해보자

HeapMemory

stack and heap

stack

byte, short, int, long, boolean, char 타입의 데이터들이 할당되어 데이터가 저장된다.
stack은 OS에 의해 관리가 된다.

heap

Integer, String, ArrayList, Object 타입의 데이터들이 올라간다.
프로그램에 의해 관리

heap 구조

메모리에 데이터가 올라가면 처음에는 Eden 영역으로 생성이 됩니다.
이후 GC 에따라 Surivor1,2 로 이동 후 최종으로 Old에 저장되게 됩니다.

사용 빈도에 따른 참조 여부에 따라 Survivor1,2로 이동후 Old로 이동하게 됩니다.

GC의 역할

GCgarbage collection의 단축어로 메모리에 더 이상 사용하지 않는 정보를 정리하는 역할이다.
Heap Memory 에 더 이상 사용하지 않아 낭비되는 메모리를 정리해주는 것

GC 종류

Minor GCEden, Survivor1,2 메모리를 정리합니다.
Eden영역 메모리가 가득차게 되면 Minor GC가 동작합니다.

  • Eden -> Survivor1 또는 Survivor2로 이동
  • Survivor1 <-> Survivor2 데이터 이동
    • 데이터가 Survivor1,2 동시에 적재 되지 않고 Survivor1 -> Survivor2로 이동할때 모든 데이터를 넘겨주게 됩니다. (둘 중 하나만 데이터가 적재)
    • GC 실행마다 Survivor1,2 안에서 데이터를 이동시키며 정리
  • Survivor1,2에서 오래된 데이터를 Old로 이동

Full GCOld 메모리를 정리합니다.
Old영역 메모리가 가득차면 Full GC가 동작합니다.
동작 시, 어플리케이션이 멈추게 됩니다 (Stop the world)

GC 모니터

그러면 서버에서 GC 가 처리를 지켜보자

jstat -gcutil PID 1000 (1000은 1초에 한번씩 Update)

S0, S1, E, O 각각 위에서 설명 한 Heap 메모리의 앞 자리만 표시
중간쯤 Minor GC가 실행되어 데이터가 정리되면서 Survivor 영역이 옮겨진게 확인이 됩니다.

Error 원인

다시 돌아와서 OutOfMemoryErrorHeap공간이 부족해서 발생으로 추측이된다.
그렇다면 Heap 공간이 왜 부족했을까

거기에 대한 해답은 GC overhead limit exceeded 에서 파악이 가능했다.
위 에러가 발생한 경우는 GC 수행에 98% 이상을 소비, Heap 메모리가 2% 미만 복구
정리할 데이터가 없이 모두 사용중이라는 것

그렇다면 어디서 많은 메모리를 잡고 있을까

업로드 된 Excel를 읽고 데이터를 가공 중 문제가 발생한 것

while (rowIndex < worksheet.getPhysicalNumberOfRows()) {
--생략--
       if (row != null && row.getCell(0) != null) {
            rowIndex++;
--생략--                

업로드된 Excel A열의 데이터만 필요하여, A열만 데이터를 가져오도록 되어있다.
while의 탈출 조건을 보면 worksheet.getPhysicalNumberOfRows() 보다 값이 커야한다

아래의 엑셀을 업로드 시 getPhysicalNumberOfRows() 값은 11이 됩니다
하지만 A의 열의 값이 있어야지 rowIndex값을 올릴 수 있다.

무한루프에 빠져 heap 메모리가 부족해졌다
테스트 서버여서 파악하기 쉬웠지만, 이용량이 많은 사이트였다면 원인 파악하기 쥐약일 것 같다.

profile
LEVEL UP

0개의 댓글