가비지 컬렉션 최적화
최적화가 필요한 이유?
Java는 GC를 통해 메모리를 자동으로 관리하는 기능을 제공합니다. 이는 개발자에게 메모리 할당 및 해제 작업의 복잡성을 덜어주는 큰 이점을 제공합니다. 하지만, 다음과 같은 사항들로 인해 GC 동작을 이해하고 효율적인 메모리 관리와 JVM 튜닝을 통해 GC를 최적화하는 것이 필요할 수 있습니다.
- Stop-the-World 문제
- GC를 수행하는 동안 JVM은 애플리케이션의 모든 스레드를 일시 정지함
- 사용자는 지연이나 끊김 현상을 경험할 수 있음
- Heap 사이즈 증가
- Java의 발전과 더불어 JVM의 Heap 영역의 사이즈도 점차 증가
- Heap 영역의 사이즈가 클수록 메모리를 검사하고 정리하는 시간 증가
- Major GC의 경우 특히 수행 시간이 길어져 응답 지연이 발생할 수 있음
- GC 빈도 및 효율성
- Minor GC는 빈번하게 발생하지만, 수행 시간이 짧아 애플리케이션 성능에 미치는 영향이 작음
- Major GC는 발생 빈도가 낮지만, 메모리 단편화 해결 및 Old Generation 청소로 인해 더 많은 CPU와 시간 소모
- 메모리 단편화 문제
- Old Generation에서 객체가 생성되고 삭제되면서 단편화가 발생
- 메모리가 단편화되면, 연속된 빈 공간을 확보하지 못해 메모리 사용이 비효율적
- GC 수행 시간이 길어질 뿐 아니라 OutOfMemoryError가 발생할 가능성도 증가
가비지 컬렉션 알고리즘
알고리즘의 역할
가비지 컬렉션 알고리즘과 최적화는 긴밀히 연결되어 있습니다. GC 알고리즘의 특성과 애플리케이션 요구 사항에 따라 GC가 애플리케이션 성능에 미치는 영향이 달라지기 때문에, 최적화는 알고리즘 선택에서 시작됩니다. 이를 구체적으로 살펴보겠습니다.
Stop-the-World 중심 GC
Serial GC
Serial GC는 JVM에서 제공하는 가장 단순한 가비지 컬렉션 알고리즘으로 다음과 같은 특징을 가지고 있습니다.
- Java 초기 버전 기본 알고리즘
- 단일 스레드 처리
- Stop-the-World 방식
- GC가 실행되면 애플리케이션의 모든 스레드가 멈춤
- 작은 Heap 환경에 적합
- Heap 크기가 작을수록 빠르게 수행되며, 메모리 관리가 단순한 애플리케이션에 적합
- CPU 리소스가 제한된 환경이나 테스트 목적으로 자주 사용
Parallel GC
Parallel GC는 Throughput을 극대화하기 위해 설계된 가비지 컬렉션 알고리즘으로 다음과 같은 특징을 가지고 있습니다.
- Java 8 이하 버전에서 기본 GC 알고리즘으로 사용
- Throughput 중심 설계
- 여러 스레드를 병렬로 사용하여 GC 작업 수행
- Stop-the-World 시간을 줄이는 대신, GC가 차지하는 전체 시간을 최소화
- Old 영역의 객체를 병렬로 처리하는 Parallel Old GC를 지원
- Parallel GC는 Young 영역의 객체만 병렬 처리
-XX:+UseParallelOldGC 옵션으로 활성화
Concurrent 중심 GC
G1(Garbage First) GC
G1 GC는 대규모 메모리 환경에서 성능과 안정성을 모두 제공하기 위해 짧은 정지 시간을 목표로 설계된 가비지 컬렉션 알고리즘으로 다음과 같은 특징을 가지고 있습니다.
- Java 9 이상 버전에서 기본 GC 알고리즘
- 예측 가능한 짧은 정지 시간
-XX:MaxGCPauseMillis=<n> 옵션을 통해 목표 정지 시간을 설정
- Region 단위 메모리 관리
- Eden, Survivor, Old 등의 역할 동적으로 부여
- 대규모 Heap 환경에 유리
- 가비지가 많은 Region 우선 정리
- GC 작업을 효율적으로 분배하고, Major GC를 지연시키는 효과
CMS(Concurrent Mark Sweep) GC
CMS GC는 애플리케이션의 스레드와 GC 스레드가 동시에 실행되면서, Stop-the-World 시간을 최소화하기 위해 설계된 GC 알고리즘으로 다음과 같은 특징을 가지고 있습니다.
- G1 GC로 대체
- Java 9에서 Deprecated 되었고, Java 14부터 완전히 제거
- Stop-the-World 최소화
- 애플리케이션 실행과 동시에 GC 작업을 수행하여 정지 시간을 줄임
- Old Generation 대상
- CMS GC는 주로 Old Generation에서 사용되며, 객체의 생존율이 높은 환경에서 효율적
- 병렬 및 동시 작업
- GC 과정 중 일부 작업을 애플리케이션 스레드와 병렬로 수행하여 효율성을 높임
- 복잡한 GC 과정
- GC 과정이 여러 단계로 나뉘며 GC 대상을 식별하는 과정이 복잡하여 CPU 사용량이 높아질 수 있음
- 메모리 단편화 문제
- Compact 작업을 수행하지 않아 메모리 단편화가 발생할 가능성이 있음
ZGC
ZGC는 Stop-the-World 시간을 10ms 이하로 유지하도록 설계된 초저지연 GC 알고리즘으로 다음과 같은 특징을 가지고 있습니다.
- Java 11 이상 사용 가능
- 초저지연 (Ultra-Low Latency)
- Stop-the-World 시간은 Heap 크기에 관계없이 10ms 이하로 유지
- GC 작업 대부분을 동시 수행하여 애플리케이션의 중단 최소화
- 최대 16TB 크기의 메모리를 효율적으로 관리
- 대규모 데이터 처리 애플리케이션과 클라우드 환경에서 유리
- GC 작업의 병렬 수행
- Marking 및 Relocation 작업을 애플리케이션 스레드와 병렬로 처리
- Colored Pointers 사용
- 객체 참조를 관리하기 위해 Colored Pointers라는 메커니즘을 사용
- 이를 통해, 효율적인 메모리 관리와 참조 무결성을 유지
- Compact 동시 수행
- 메모리 단편화를 방지하기 위해 Compact 작업도 동시 수행
Shenandoah GC
Shenandoah GC는 낮은 정지 시간을 목표로 설계된 고성능 가비지 컬렉션 알고리즘으로 다음과 같은 특징을 가지고 있습니다.
- Java 12 이상 사용 가능
- 낮은 정지 시간
- GC 작업의 대부분을 애플리케이션 스레드와 동시 수행하여 정지 시간 최소화
-XX:MaxGCPauseMillis=<N> 옵션을 통해 목표 정지 시간 설정 가능
- 동시 압축
- GC 작업 중에도 객체를 이동(압축)하면서 메모리 단편화를 방지
- 다른 GC와 달리 Stop-the-World 상태 없이 압축 작업 수행 가능
- Heap 크기와 무관한 성능
- Heap 크기와 관계없이 일정한 정지 시간을 유지하도록 설계
- 대규모 메모리 환경에서 효율적
정리
이를 정리하면, 다음과 같습니다.
| 요구사항 | 적합한 알고리즘 | 설명 |
|---|
| 짧은 정지 시간 | G1 GC, ZGC, Shenandoah GC | 사용자 경험이 중요한 환경. Stop-the-World 시간을 최소화. |
| 높은 처리량 | Parallel GC, Parallel Old GC | 데이터 처리량이 중요한 환경. 병렬 처리를 통해 GC 시간을 최소화. |
| 작은 메모리 환경 | Serial GC | 작은 Heap 및 단일 스레드 애플리케이션에서 간단한 GC 수행. |
| 대규모 메모리 관리 | G1 GC, ZGC, Shenandoah GC | Region 기반 관리로 메모리 단편화 방지 및 효율적 관리. |
| 초저지연(10ms 이하) | ZGC, Shenandoah GC | 실시간 시스템 및 금융 거래 시스템. |
예상 질문
가바지 컬렉션의 알고리즘은 왜 중요한가요?
가바지 컬렉션은 개발자에게 메모리 관리에 대한 편리함을 주기도합니다. 하지만, Java의 발전에 따라 Heap 영역도 증가하면서 성능 저하, 메모리 단편화 문제등이 발생할 수 있습니다. 이를 해결하기 위한 작업이 JVM 튜닝이고, 이를 위해선 상황에 맞는 적합한 GC 알고리즘 선택 필요하기 때문입니다.