Java 17에서 G1GC 옵션 종류, 특징 및 튜닝 방법
Java는 오랜 기간 동안 다양한 Garbage Collector(이하 GC)를 제공해 왔으며, Java 17에서는 G1(이하 G1GC)이 기본 GC로 사용됩니다. G1GC는 초단위 이하의 짧은 GC 정지 시간(pause time)을 목적으로, 기존의 Parallel GC나 CMS(Concurrent Mark Sweep) 등의 문제점을 보완하여 고성능과 안정성을 확보하고자 만든 가비지 컬렉터입니다. 이번 블로그 게시물을 통해 G1GC가 어떻게 동작하고, 어떤 옵션들을 활용할 수 있으며, 규모가 다른 환경에서 어떻게 튜닝할 수 있는지 알아보겠습니다.
G1GC의 기본 작동 원리
G1GC(Garbage-First Garbage Collector)는 힙(Heap)을 여러 “Region”으로 나누어, 살아 있는 객체의 양에 따라 우선순위가 높은 Region부터 먼저 청소(컬렉션)하는 방식으로 동작합니다. 다음은 주요 특징입니다:
-
Region 단위 관리
크게 한 덩어리의 힙을 여러 개의 작은 Region으로 분할합니다. 각 Region은 기본적으로 동일한 크기를 갖지만(기본 크기는 1MB ~ 32MB까지 자동 결정됨), G1GC는 각 Region을 독립적으로 청소해 메모리 단편화를 완화합니다. -
Concurrent & Incremental Collection
Stop-the-world(이하 STW) 시간을 줄이기 위해 앱 스레드와 동시에(Concurrent) 백그라운드로 작업을 진행합니다. 따라서 메모리 사용량이 늘어나도, 짧은 간격으로 GC를 나누어 수행해 긴 시간 멈추는 일을 최소화하려 합니다. -
Pause Time 목표 설정
G1GC는 -XX:MaxGCPauseMillis-XX:MaxGCPauseMillis 을 통해 GC 정지 시간의 목표치를 설정할 수 있습니다. 가능한 한 설정된 목표 시간 안에 GC가 끝나도록 Region을 선택해 부분 수집(Partial Collection)을 수행합니다. -
Garbage-First 전략
한 번의 GC 마다 여러 Region 중에서 가장 부채비율(survivor ratio)이 낮거나, “수집 가치가 높다”고 판단되는 영역부터 우선적으로 수집합니다. 이를 통해 전체적인 힙 효율을 높이는 전략을 취합니다.
G1GC의 특징
G1GC는 다음과 같은 특징을 가지고 있습니다:
-
영역 기반 메모리 관리
G1GC는 힙 메모리를 고정 크기의 영역(Region)으로 나누어 관리합니다. 각 영역은 Eden, Survivor, Old 영역 등으로 사용됩니다. -
Predictable Pause Time
애플리케이션의 지연 시간을 최소화하기 위해 Pause Time Goal을 기준으로 가비지 컬렉션 작업을 조정합니다. -
Concurrent Marking
힙 영역 중에서 가비지가 많이 포함된 영역을 우선적으로 정리하여 효율적인 메모리 관리를 제공합니다. -
Evacuation Pause
복사 기반의 GC 작업으로 Fragmentation 문제를 최소화합니다.
G1GC 주요 옵션 및 기본값
아래 표는 G1GC에서 자주 쓰이는 옵션과 기본값, 그리고 간단한 설명을 정리한 것입니다.
옵션 | 기본값 | 설명 |
---|---|---|
-XX:+UseG1GC | Java 17 이후 기본 활성화 | Java 17의 기본 GC. 다른 GC와 혼용하지 않고, G1GC를 명시적으로 사용하려 할 때 지정할 수 있음. |
-XX:MaxGCPauseMillis= | 200ms | STW(Stop-The-World) 시간의 목표치를 밀리초로 설정. 예) -XX:MaxGCPauseMillis=200 ⇒ 최대 200ms를 목표로 |
-XX:InitiatingHeapOccupancyPercent= | 45 | 힙 사용률이 특정 %에 도달했을 때 백그라운드(Concurrent) GC 사이클을 시작. 기본값(45)보다 낮추면 GC를 더 일찍 시작해 메모리 고갈을 예방. |
-XX:G1ReservePercent= | 10 | GC 후에도 여유 Region을 유지해 급격한 객체 생성을 대비. 너무 높으면 실제 사용 가능한 힙이 줄어듦. |
-XX:G1HeapRegionSize= | 자동 결정 (1MB ~ 32MB) | Region 하나의 크기를 수동으로 지정할 때 사용. 일반적으로는 자동 결정이 적절함. |
-XX:+ParallelRefProcEnabled | true (기본적으로 활성화) | Reference(Soft, Weak 등) 처리를 병렬화하여 GC 시간을 단축. |
-XX:+DisableExplicitGC | false (비활성화) | System.gc() 호출을 무시해 불필요한 GC STW를 방지. G1GC 환경에서는 권장되는 설정이나, 필요에 따라 조정 가능. |
시스템 규모별 튜닝 방법 요약
다음 표는 애플리케이션 메모리 크기와 트래픽 수준에 따라 G1GC를 어떻게 조정할 수 있는지 간단히 제시합니다. 실제로는 애플리케이션 특성, 자원 환경, JVM 로그 분석 결과 등을 종합적으로 판단해야 합니다.
시스템 규모 | 주요 전략 | 상세 설명 |
---|---|---|
소규모 서비스 (수백 MB ~ 수 GB) | 기본 옵션 사용 | Java 17 기본 G1GC 설정을 그대로 사용해도 무난함. GC 로그 모니터링( -Xlog:gc* )으로 STW 시간과 빈도를 관찰하며, 문제 없으면 유지. |
중간 규모 서비스 (수 GB ~ 수십 GB) | -XX:MaxGCPauseMillis=200 정도로 시작 -XX:InitiatingHeapOccupancyPercent=30~40 |
응답 시간을 중요시한다면 100~200ms 정도 STW 목표를 잡고 모니터링. 트래픽 변동이 커질 때를 대비해 기본값(45)보다 낮은 Initiating Heap Occupancy Percent를 적용, GC를 자주 일찍 시작해 Full GC 방지. |
대규모 서비스 (수십 GB ~ 수백 GB) | -XX:MaxGCPauseMillis=100~200 GC 병렬 스레드(ParallelGCThreads, ConcGCThreads) 조절 -XX:G1ReservePercent=15~20 정도로 상향 |
코어 수가 많은 환경이라면 병렬 GC 스레드를 늘려서 효율 향상. 초당 대규모 객체 생성이 발생할 수 있으므로 Reserve Percent를 넉넉히 잡아 Full GC 위험을 줄임. STW 목표를 엄격하게(100ms 등) 설정하고 단계적으로 검증. |
효과적인 모니터링과 진단
- GC 로그 분석
“Young GC” 발생 빈도, “Full GC” 발생 여부, STW 시간을 확인하면서 튜닝 효과를 측정합니다.-Xlog:gc*:file=./gc.log:time
-
Survivor 영역 확인
살아남는 객체가 많은지, Old 영역으로 빠르게 쌓이지 않는지 관찰하여 Survivor 영역이 충분한지 점검합니다. - 메타스페이스(Metaspace) 사용량 체크
클래스 로딩이 매우 빈번한 환경에서는 메타스페이스 사용이 증가할 수 있으므로, -XX:MaxMetaspaceSize 등도 함께 고려합니다.
마무리 및 결론
G1GC는 짧은 GC 정지 시간을 유지하면서도, 힙을 효율적으로 다루도록 설계되었습니다. Java 17에서 기본 GC로 이미 충분히 안정적으로 동작하지만, 시스템 규모와 서비스 특성에 맞춰 적절히 옵션을 조정하면 한층 더 나은 성능과 응답 속도를 기대할 수 있습니다.
- 먼저 기본 설정과 기본값을 충실히 사용
- 문제가 확인되면 한 가지씩 설정값 변경 후 로그 분석
- 정기적인 모니터링과 부하 테스트를 통해 튜닝 효과 확인
이러한 절차를 지키면, G1GC로 더욱 쾌적한 Java 애플리케이션 환경을 구축할 수 있을 것입니다.
참고자료
- Oracle Java Documentation - https://docs.oracle.com/en/java/
- G1GC Performance Tuning Guide - https://www.oracle.com/technetwork/java/javase/g1gc-tuning-5-2009248.html
- Java 17 G1GC Overview - https://www.baeldung.com/java-g1gc
댓글남기기