문제 정의

검증 환경

본 절은 원문 사례·문서에 등장한 버전/도구를 정리한 것이다. 별도 실험실 재현이 명시되지 않은 항목은 일반화하지 않는다.

항목본문·원문에서 확인된 범위
런타임/도구Java 11
런타임/도구Java 8
런타임/도구Java 6
문서 정리일2026-09-01
  • OS/CI 세부 값은 프로젝트마다 다르므로, 적용 전 로컬에서 동일 오류 메시지를 재확인한다.

다음과 같은 에러가 JUnit 테스트 실행 중에 발생한다.

1
java.lang.OutOfMemoryError: GC overhead limit exceeded

OutOfMemoryError가 무엇인지는 알지만, GC overhead limit이 어떤 뜻인지, 그리고 어떻게 해결해야 하는지가 핵심이다. 이 오류는 단순히 “메모리가 부족하다"는 의미를 넘어, JVM 내부의 가비지 컬렉터(GC)가 더 이상 제 역할을 하지 못하는 상태라는 신호다.

증상 fingerprint

  • 에러 메시지: java.lang.OutOfMemoryError: GC overhead limit exceeded
  • 발생 단계: 주로 JUnit 테스트 실행, 대량 데이터 처리, 애플리케이션 운영 중
  • 관련 도구: JVM, 가비지 컬렉터(Parallel / CMS / G1)
  • 흔한 오해: “그냥 메모리가 부족하니 힙만 키우면 된다"로 오인함
  • 빠른 판단 기준: 힙을 키워도 반복된다면 메모리 누수일 가능성이 높다

표면 증상

표면 증상은 “GC가 지나치게 많은 시간을 쓰는” 것이다.

  • 프로세스가 실제 작업 없이 가비지 컬렉션만 반복한다.
  • 프로그램 실행이 멈춘 것처럼 보인다.
  • 콘솔/로그에 GC overhead limit exceeded가 반복 표시된다.

JVM이 “과도한 GC” 상태를 판정하는 기본 임계값은 다음과 같다.

GC 판정 항목기본 임계값
GC에 소비하는 CPU 비율전체 CPU 시간의 98%
GC당 힙 회수량힙의 2% 미만

이 경우 GC가 CPU를 독점하면서도 힙 공간을 확보하지 못하므로, JVM은 프로그램이 기능을 완수하지 못하는 것을 막기 위해 이 오류를 던진다.

원인 추적

가능한 원인 후보를 직접 확인해 보자.

원인 후보확인 방법맞는 경우의 증상해결 방향
힙 크기 부족-Xmx 설정과 실제 메모리 사용량 확인힙을 키우면 잠시 동작힙 크기 조정
메모리 누수힙 덤프(jmap, Eclipse MAT) 분석시간이 지날수록 메모리 사용량 증가참조 정리 / 미사용 객체 해제
임시 객체 과다 생성프로파일러로 할당량 확인짧은 생명주기 객체가 다수코드 최적화
GC 알고리즘 선택-XX:+PrintGCDetails 로그특정 GC에서만 재현GC 튜닝

흔한 주범은 “매우 작은 메모리 환경에서 수많은 임시 객체와 weak-referenced 객체를 생성"하는 코드이다. (출처: StackOverflow 채택 답변)

근본 원인

이 오류의 본질은 GC가 힙을 거의 회수하지 못하면서 CPU만 소비하는 상황이 지속되는 것이다. JVM은 프로그램이 실제 작업을 완수하지 못한 채 가비지 컬렉션에만 시간을 쓰는 상태를 막기 위해 의도적으로 오류를 던진다.

핵심은 두 가지다.

  1. 메모리 환경이 이미 한계에 도달 — 힙이 가득 차서 새 객체를 위한 공간이 없다.
  2. 만들어지는 쓰레기(가비지)를 GC가 제대로 회수하지 못함 — 임시/weak 참조 객체가 누적되어 회수량이 급감한다.

즉, 단순히 “GC가 느리다"는 것이 아니라 애플리케이션이 힙을 고갈시키는 근본 원인(누수 또는 과도한 할당)이 존재한다는 신호다.

코드 해결책

1. 먼저 힙 크기 조정

같은 상황을 일부 막아주지만, 근본 원인은 아니다.

1
java -Xmx512m -XX:+HeapDumpOnOutOfMemoryError -jar myapp.jar

-Xmx는 힙의 최대 크기를 지정한다. 512m, 1g, 2g로 점차 늘려 보며 더 이상 오류가 나지 않는지 확인한다. 단, 힙을 늘리는 것은 임시 방법이며 근본 원인 해결이 아니다.

2. 코드 임시 객체(쓰레기) 최적화

이미 한계에 다다른 메모리 환경에서 대량의 임시 객체를 만드는 코드를 최적화한다. 예를 들어 루프 안에서 불필요하게 객체를 반복 생성하지 않도록 재사용한다.

3. 메모리 누수 진단

가장 확실한 것은 힙 덤프를 떠서 누수를 확인하는 것이다.

1
2
3
4
5
# 오류 발생 시 힙 덤프 자동 생성
java -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -jar myapp.jar

# 생성된 java_pid<pid>.hprof 파일을 Eclipse MAT나 VisualVM으로 열어
# Leak Suspects / Dominator Tree를 확인

4. GC 튜닝 가이드 참고

Java 버전별 공식 GC 튜닝 가이드의 “과도한 GC(excessive GC)” 섹션을 참고한다. Java 11, Java 8, Java 6 튜닝 가이드가 대표적이며, Parallel / CMS / G1 컬렉터별 구체적인 내용을 포함한다.

다음 검증 명령으로 해결 여부를 확인한다.

1
2
3
4
5
# GC 동작 로그 확인
java -Xmx1g -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar myapp.jar

# 동일 테스트 재실행으로 재발 여부 확인
./gradlew test

향후 예방 조치

  • 임시/weak 참조 과다 생성을 코드리뷰와 프로파일링으로 주기적으로 점검한다.
  • 메모리 누수를 탐지하기 위해 CI에서 힘 덤프 로깅(-XX:+HeapDumpOnOutOfMemoryError)을 활성화한다.
  • 힙을 늘리는 해결책은 임시 방법임을 기억한다. 근본 원인은 누수나 과한 할당이므로 누수를 제거한다.
  • GC 알고리즘별 동작 차이와 튜닝 절은 공식 가이드에서 확인한다. (환경에 따라 다를 수 있다.)
  • JUnit처럼 메모리가 넉넉하지 않은 환경에서는 테스트 데이터 양을 최소화한다. (일반적인 점검 기준)

DevTrace verdict

이 문제의 핵심은 CPU 시간을 지나치게 쓰면서도 힙의 회수량이 거의 없는 “메모리 고갈 신호"이다. 따라서 힙 크기를 키우는 것은 증상만 땜질할뿐, 실제 해결은 근본 원인(누수·과다 할당)을 제거하는 것이다.

DevTrace 결론

GC가 98%의 CPU 시간을 쓰면서 회수는 2% 미만인 상태는 메모리가 곧 고갈될 것이라는 신호이므로, 힙을 늘리기보다 힙 덤프로 근본 원인을 진단해야 한다.

원문 출처는 문제 발견의 단서이며, 위 판단과 점검 항목은 DevTrace의 독자 분석이다.

출처