인메모리 워크로드에서 발생하는 GC 지연과 OOM의 원인 진단, 실무 적용 가능한 JVM 설정과 운영 체크리스트를 단계별로 정리.
이 문서는 대용량 데이터를 메모리에서 처리하는 시스템(인메모리 캐시·실시간 분석·스트리밍 처리 등)을 운영하는 엔지니어·SRE·플랫폼 기획자를 대상으로 한다. 수집 가능한 지표, 로그 수집 방법, GC 선택 가이드, OOM 유형별 진단 절차와 운영 전환 체크포인트을 중심으로 실무 적용 가능한 절차를 제시한다.
주요 내용
매일 엑셀 반복 작업에 시달리던 실무자 A씨는 인메모리 캐시를 도입한 뒤 응답 지연과 간헐적 OOM이 발생했다. 첫 점검 항목은 다음과 같다.
- 애플리케이션 로그 및 GC 로그 수집이 활성화되어 있는가? (Xlog 또는 -Xlog:gc 옵션)
- OOM 발생 시점의 스택·힙 스냅샷(Heap dump)이 확보되어 있는가?
- 메모리 유형(Heap, Metaspace, Direct, Native)별 사용량 추이가 수집되고 있는가?
- JVM 버전과 적용된 GC 알고리즘이 무엇인지 확인했는가?
데이터 수집 전 최소 명령: 프로덕션에 위험을 주지 않는 범위로 jcmd, jmap, jstack을 사용해 스냅샷을 확보한다. 예: jcmd

사례 분석: OOM 재현과 근본 원인 도출 흐름
기획자 B씨 사례: 배치 성격의 데이터 적재 후 30분~2시간 내 메모리가 점진적으로 증가해 OOM이 발생했다. 원인 규명 절차는 다음 단계로 압축된다.
- GC 로그 획득(최소 24~72시간) 및 GC 유형별 pause/throughput 지표 산출
- 힙 덤프(e.g., jmap -dump:live,format=b,file=heap.hprof) 생성 및 Eclipse MAT로 힙 오브젝트 레이크 분석
- Native 메모리/Direct buffer 사용 추적 (NMT, Native Memory Tracking 활성화)
- 스레드 덤프(jstack)로 잠김/대기 상태와 객체 보유 경로 확인
- 애플리케이션 코드에서의 캐시 무제한 증가 포인트(무한 큐, Map 키 누적 등) 확인
실무 체크포인트: 힙 덤프에서 가장 큰 객체 그룹(예: byte[]로 된 직렬화 바이트, String 인스턴스, 컬렉션 내부 엔트리)을 우선 조사한다. 컬렉션 크기 증가가 원인이라면 코드·설계 차원에서 TTL/사이즈 제한을 도입해야 한다.
힙 덤프를 수집할 때는 프로세스 중단을 최소화하기 위해 -XX:+HeapDumpOnOutOfMemoryError를 설정하고, 가능하면 별도의 관리 인터페이스로 원격 덤프를 수집하도록 구성하라.

데이터 비교: 튜닝 전/후 운영 효율 변화
| 지표 | 튜닝 전 | 튜닝 후 | 변화량 |
|---|---|---|---|
| 평균 응답 지연(99%ile) | 180 ms | 32 ms | -82% |
| GC 전체 CPU 비율 | 18% | 6% | -66% |
| 평균 GC Pause | 150 ms | 8 ms | -95% |
| OOM 발생 빈도(월) | 3회 | 0회 | -100% |
JVM GC 선택 및 구성 가이드
최신 공식 기술 문서에 따르면 GC 선택은 힙 크기, 허용 지연, CPU 가용성, 객체 할당 패턴에 따라 달라진다. 아래는 실무에서 자주 비교되는 GC들에 대한 요약이다.
| GC | 주요 장점 | 단점 / 제한 | 권장 워크로드 |
|---|---|---|---|
| G1 | 예측 가능한 중간지연, 힙 파편화 완화 | 대형 힙에서 낮은 지연 요구시 한계 | 웹서비스, 캐시(수십 GB 영역) |
| ZGC | 매우 낮은 pause(수 ms 이하), 대형 힙 지원(테라바이트) | 초기 JDK 버전 호환성, 메모리 오버헤드 존재 | 초저지연, 대용량 인메모리 |
| Shenandoah | 저지연 동작, 부분적인 동시 압축 | GC 튜닝 매개변수 복잡성 | 실시간 분석·트레이딩 등 |
| Parallel (Throughput) | 높은 처리량, 단순 설정 | GC pause 큐 옵셋 큼 | 배치 처리, 백그라운드 작업 |
구체적 권장 설정 예시(프로덕션 가이드라인):
- 대형 힙(>64GB) & 저지연 요구: ZGC 또는 Shenandoah 사용 고려, -Xmx/ -Xms 일치 설정
- 일반 웹서비스: G1 기본, -XX:MaxGCPauseMillis=100, -XX:InitiatingHeapOccupancyPercent=35 조정
- 메모리 집약형 캐시는 가능한 경우 오프-힙(Direct) 또는 native allocator 사용하여 GC 부담 완화
테스트 중 발견된 주의사항
테스트 환경에서 잘 동작하던 GC 설정이 프로덕션에서 실패하는 주요 원인:
- 테스트 데이터가 실제 객체 라이프사이클과 다른 경우(예: 테스트는 단발성 요청만 수행)
- 동시성 패턴 차이(프로덕션은 높은 동시 할당률로 인해 GC 압박 증가)
- 운영 환경의 메모리 오버커밋/NUMA 특성 무시
- JVM 버전 차이로 인한 GC 구현 차이
운영 전 점검 리스트:
- 프로덕션과 유사한 스케일로 부하를 재현했는가?
- HeapDump 및 GC 로그가 중앙 수집(ELK/Prometheus + Grafana)으로 자동화되어 있는가?
- OOM 발생 시 자동 재기동 정책과 포스트모템 수집(로그·덤프)이 구성되어 있는가?
프로덕션에 ZGC/Shipandoah를 도입할 때는 메모리 오버헤드와 CPU 사용량 변화를 A/B로 모니터링하라. 단일 인스턴스에서만 확인하지 말고, 서비스 디스커버리·로드밸런싱 하에서의 동작을 검증해야 한다.
운영 단계에서의 지속적 관찰 지표
최신 공식 기술 문서와 현장 사례를 종합한 권장 지표 목록:
- GC 메트릭: young/old GC 카운트, pause 시간(평균/최대/99%tile), promotion 실패 수
- 메모리 메트릭: Heap used, Metaspace used, Direct memory used, Native memory 추적
- 애플리케이션 메트릭: allocation rate (MB/s), object churn rate, 큐/캐시 사이즈
- 시스템 메트릭: CPU steal, context switch, NUMA imbalance
알람 임계값 예시:
- GC pause(99%ile) > 200ms 지속 5분: 경고
- Promotion 실패 발생 또는 Full GC 빈도 급증: 즉시 조사
- Direct memory 사용량 > 80% 한 노드: 용량 증설 또는 할당 패턴 조정
유용한 도구 및 공식 자료
실제 진단에 자주 사용되는 도구와 공식 문서 링크.
🔗 OpenJDK Garbage Collector Overview
🔗 GCViewer (GitHub) – GC 로그 시각화 도구
🔗 jcmd 매뉴얼
💡 API 비용 최적화 실전 체크리스트
운영 전 권장 실험 설계(체크리스트): 최소 2주 이상 GC 로그 수집, 힙 덤프 자동화, 부하 재현 스크립트(성능·동시성), 모니터링 대시보드(미리 정의된 알람 포함)를 준비하라. 최신 버전의 JDK 릴리즈 노트를 확인해 GC 관련 변경사항을 사전에 파악하라.