본문 바로가기
언어/Java

[Java] JVM GC 최적화 경험과 인사이트

by Geunny 2025. 3. 1.
반응형

1. 들어가며

어느 날, 운영 중인 애플리케이션에서 갑작스럽게 응답 지연이 발생했다. 시스템 모니터링 결과, GC가 과도하게 실행되면서 STW(Stop-The-World) 시간이 증가하고 있었다. 처음에는 단순한 Minor GC 문제로 보였지만, 자세히 분석해보니 Old 영역에서 발생하는 Major GC와 Full GC가 원인이었다. 이 문제를 해결하기 위해 Heap Dump 분석, GC 로그 모니터링, JVM 옵션 튜닝을 진행하며 얻은 인사이트를 공유하고자 한다.


2. JVM의 GC 종류와 특성

GC의 종류 및 동작 방식

GC는 JVM의 메모리 관리를 자동화하는 핵심 기능이지만, 잘못 설정하면 애플리케이션 성능에 심각한 영향을 미칠 수 있다. 대표적인 GC 방식은 다음과 같다.

  1. SerialGC
    • 단일 스레드로 GC 수행 (STW 시간이 길다)
    • 주로 단순한 애플리케이션이나 작은 메모리 환경에서 사용됨
  2. ParallelGC (JDK 8 기본 GC)
    • Young GC를 병렬로 처리하여 STW 시간을 줄임
    • -XX:+ParallelGCThreads=n 옵션으로 GC 스레드 수 설정 가능
    • ParallelOldGC를 사용하면 Old 영역도 병렬로 처리 가능
  3. CMS (Concurrent Mark-Sweep) GC
    • STW 시간을 최소화하기 위해 Old 영역을 동시 처리
    • 단점: CPU 부하 증가, Compaction 기능 부족 → 메모리 단편화 발생 가능
  4. G1GC (JDK 9 이후 기본 GC)
    • Region 단위로 메모리를 관리하여 효율적인 GC 수행
    • Mixed GC를 통해 Old 영역을 점진적으로 정리하여 Full GC 발생을 줄임
    • Humongous 객체를 위한 전용 영역이 존재
  5. ZGC / Shenandoah GC (JDK 11 이상)
    • 초저지연 GC, STW 시간이 10ms 이하로 유지됨
    • JDK 8 기반 환경에서는 고려 대상에서 제외됨

GC의 Stop-The-World(STW)

GC는 메모리를 정리하는 동안 애플리케이션을 일시적으로 멈춘다. 이를 STW(Stop-The-World)라고 하며, GC 알고리즘에 따라 다음과 같이 다르게 발생한다.

  • SerialGC: STW를 단일 스레드로 처리 → 지연 시간이 가장 김
  • ParallelGC: 멀티스레드로 Young GC 수행 → 속도 개선
  • CMS GC: Old 영역을 동시 마킹하여 STW 감소
  • G1GC: Region 기반 GC로 STW를 최소화하려 함

3. GC의 기본 동작 원리

GC의 동작 단계

GC는 크게 Mark & Sweep & Compaction 과정을 거친다.

  1. Mark 단계: GC가 Stack 변수를 탐색하며 참조되는 객체를 표시
  2. Sweep 단계: Mark 되지 않은 객체를 정리하여 메모리 확보
  3. Compaction 단계: 메모리를 단편화되지 않도록 정리하여 연속적으로 배치

객체의 이동 경로

JVM에서 객체는 Young → Survivor → Old 영역으로 이동한다.

  • Young 영역에서 생성된 객체가 살아남으면 Survivor 영역으로 이동
  • Survivor 영역에서 MaxTenuringThreshold 이상 살아남으면 Old 영역으로 승격
  • Old 영역이 가득 차면 Major GC 발생

Minor GC / Major GC / Full GC 차이점

  • Minor GC: Young 영역이 가득 차면 발생 (빠름)
  • Major GC: Old 영역이 가득 차면 발생 (느림, STW 영향 큼)
  • Full GC: Heap 전체를 정리 (Young + Old + Metaspace) → 가장 느림

4. Metaspace와 메모리 관리

Metaspace란? (JDK 8 이후 도입)

  • 기존의 PermGen을 대체하는 JVM의 네이티브 메모리 영역
  • 클래스 메타데이터(Class Metadata) 저장
  • 기본적으로 Full GC에서만 정리되지만, 일부 GC(G1GC, ZGC)에서는 Major GC 중에도 정리될 수 있음

Metaspace 관리 최적화

  • -XX:MetaspaceSize  -XX:MaxMetaspaceSize 옵션 설정으로 메모리 사용 조절
  • -XX:+ClassUnloadingWithConcurrentMark 활성화하여 클래스 언로드 최적화

5. G1GC 최적화 경험

운영 환경에서는 JDK 8의 기본 GC인 ParallelGC 대신, STW 시간을 줄이기 위해 G1GC를 선택했다. 그러나 G1GC도 특정 상황에서는 Full GC가 발생할 수 있다.

G1GC에서 Full GC가 발생하는 경우

  1. Humongous 객체가 과도하게 많음 → Region을 초과하는 큰 객체 할당 문제
  2. Mixed GC가 Old 영역을 충분히 정리하지 못함 → Full GC로 전환
  3. Metaspace 부족 → 클래스 언로드 필요
  4. Region 부족 → 새로운 객체 할당이 불가능

GC 최적화 적용 사례

 Heap Dump 및 GC 로그 분석

  • jmap -dump:format=b,file=heap_dump.hprof 활용하여 불필요한 객체 확인
  • Eclipse MAT을 사용하여 객체 참조 문제 분석

 UseStringDeduplication 적용

  • Thread Dump에서 new String(byte[]) 호출이 과도하게 발생하는 것을 발견
  • -XX:+UseStringDeduplication 옵션 활성화하여 메모리 사용 최적화

 GC 튜닝 옵션 적용

  • -XX:G1HeapRegionSize=8m → Region 크기 조절
  • -XX:InitiatingHeapOccupancyPercent=30 → Mixed GC 조기 실행

6. 마무리하며

GC 최적화는 단순한 JVM 옵션 변경이 아니라, 애플리케이션의 메모리 패턴을 깊이 이해하고 실험을 통해 개선해야 한다. Heap Dump 및 GC 로그를 활용하여 시스템의 메모리 문제를 파악하고, 적절한 GC 정책을 선택하는 것이 중요하다. 앞으로도 지속적으로 GC 최적화를 연구하며 성능을 개선해 나갈 계획이다.

댓글