ZGC (Z Garbage Collector) - 초저지연 GC의 원리
Java GC를 이야기하면 항상 등장하는 문장이 있다.
"ZGC는 Pause Time이 1ms 이하래."
이 말은 대부분 사실이다.
하지만 많은 개발자가
"그럼 G1보다 무조건 좋은 GC 아닌가요?"
라고 생각한다.
그건 아니다.
GC는
"얼마나 빠른가?"
보다
"어떤 환경에서 가장 적합한가?"
가 훨씬 중요하다.
이번 글에서는
- ZGC가 등장한 이유
- G1과 근본적인 차이
- Colored Pointer
- Load Barrier
- Concurrent Relocation
- Pause Time이 짧은 이유
- 실무에서 언제 사용하는가
까지 알아본다.
왜 ZGC가 등장했을까?
G1도 상당히 좋은 GC다.
하지만 Heap이 계속 커지면 문제가 생긴다.
예를 들어
Heap
256GB
또는
Heap
1TB
이런 환경에서는
아무리 Region으로 나눠도
Mark
Relocation
Reference Update
등의 작업이 점점 부담된다.
대규모 금융 시스템
실시간 광고 플랫폼
대용량 캐시 서버
AI 플랫폼
같은 서비스는
Heap이 수백 GB까지 커질 수 있다.
이런 환경에서
수십 ms의 Pause도
문제가 될 수 있다.
그래서 등장한 것이
ZGC
이다.
ZGC의 목표
G1의 목표
Pause Time
약 200ms
ZGC의 목표
Pause Time
1ms 이하
Heap이
4GB
64GB
512GB
1TB
가 되어도
Pause Time을 거의 일정하게 유지하는 것이 목표다.
G1과 가장 큰 차이
G1도
Concurrent Marking을 한다.
하지만
일부 작업은
Stop-The-World가 필요하다.
반면
ZGC는
거의 모든 작업을
애플리케이션과 동시에 수행한다.
즉
Application
↓
GC
↓
동시에 실행
그래서
Pause가 매우 짧다.
기존 GC의 문제
예를 들어
객체 이동이 필요하다.
기존 GC는
객체 이동
↓
모든 참조 수정
↓
재시작
이 필요하다.
Heap이 클수록
참조 수정 시간이 길어진다.
ZGC는
이 과정을 다르게 처리한다.
Colored Pointer
ZGC의 핵심 기술
일반적인 객체 참조
0x12345678
메모리 주소만 가진다.
ZGC는
참조 자체에
메타데이터를 넣는다.
개념적으로는
주소
+
GC 상태 정보
를 가진다.
이를
Colored Pointer
라고 한다.
예를 들어
100101010101...
비트 일부는
객체 주소
나머지는
Mark 여부
Relocation 여부
Remapped 여부
같은 상태를 저장한다.
즉
객체 자체가 아니라
참조가
현재 상태를 알고 있다.
왜 이렇게 할까?
기존 GC는
객체가 이동하면
모든 참조를 찾아야 한다.
객체 이동
↓
참조 전부 수정
ZGC는
참조를 읽는 순간
자동으로
최신 위치를 확인한다.
즉
참조 수정
↓
나중에
필요할 때
수행한다.
Load Barrier
이것이 ZGC의 핵심이다.
객체를 읽을 때마다
특수한 Barrier가 실행된다.
예를 들어
user.getName();
실제로는
개념적으로
Load Barrier
↓
주소 확인
↓
필요하면 최신 주소 변경
↓
객체 접근
이 된다.
즉
객체를 읽는 순간
자동으로
올바른 객체를 찾아간다.
그래서
GC가
모든 참조를
한 번에 수정할 필요가 없다.
Concurrent Relocation
기존 GC
객체 이동
↓
애플리케이션 정지
↓
참조 수정
↓
재시작
ZGC
객체 이동
↓
애플리케이션 계속 실행
↓
Barrier가
최신 주소 제공
즉
객체 이동도
동시에 수행한다.
왜 Pause가 짧을까?
대부분의 작업을
Concurrent로 수행하기 때문이다.
예를 들어
Mark
Reference Update
Relocation
전부
백그라운드에서 수행된다.
Stop-The-World는
정말 최소한의 작업만 한다.
그래서
Pause가
1ms 이하까지 내려간다.
단점
CPU 사용량 증가
Concurrent 작업이 많다.
즉
백그라운드 스레드가
계속 동작한다.
작은 Heap에서는 효과가 적다
Heap
512MB
정도라면
G1과 큰 차이를 느끼기 어렵다.
구현이 복잡하다
Colored Pointer
Barrier
Concurrent Relocation
등이 추가된다.
실무에서는 언제 사용할까?
다음과 같은 환경에서 많이 사용된다.
- 초대형 Heap
- 초저지연 서비스
- 금융 시스템
- 게임 서버
- 실시간 광고 플랫폼
- AI 추론 서버
반대로
일반적인
Spring Boot
REST API
관리 시스템
전자결재
쇼핑몰
정도라면
G1으로 충분하다.
G1 vs ZGC
| 기본 GC(Java 17) | O | X |
| Pause | 수십~수백 ms | 보통 1ms 이하 |
| Heap | 중대형 | 초대형 |
| CPU | 보통 | 높음 |
| 처리량 | 높음 | 약간 낮을 수 있음 |
| 실무 사용 | 가장 많음 | 특정 분야 중심 |
Java 버전별 변화
초기 ZGC는
Linux만 지원했다.
이후
Java 15부터
정식 기능으로 자리 잡았고
현재는
Linux
Windows
macOS
모두 지원한다.
Java 17 이상에서는
실무에서도 충분히 사용할 수 있는 수준으로 안정화되었다.
ZGC 사용 옵션
-XX:+UseZGC
옵션 설명
- -XX:+UseZGC
- ZGC를 활성화한다.
Heap 설정
-Xms16g
-Xmx16g
옵션 설명
- -Xms16g
- JVM 시작 Heap을 16GB로 설정한다.
- -Xmx16g
- Heap 최대 크기를 16GB로 설정한다.
실무에서는 ZGC도 -Xms와 -Xmx를 동일하게 맞추는 경우가 많다.
G1보다 무조건 좋은가?
아니다.
예를 들어
쇼핑몰 API 서버
Heap
2GB
응답 시간
50ms
이 환경에서
ZGC를 사용해도
사용자는 거의 차이를 느끼지 못한다.
오히려
CPU 사용량만 증가할 수도 있다.
반대로
Heap
512GB
실시간 거래 시스템
Pause 20ms도 허용되지 않는 환경
이라면
ZGC가 매우 강력한 선택이 된다.
GC 선택 기준
실무에서는 보통 다음 기준으로 선택한다.
일반 웹 서비스
↓
G1
초대형 Heap
+
초저지연
↓
ZGC
배치 작업
↓
Parallel GC
즉
GC는
최신 기술을 선택하는 것이 아니라
서비스 특성에 맞게 선택하는 것이 중요하다.
지금까지의 연결
객체 생성
↓
Young Generation
↓
Minor GC
↓
Promotion
↓
Old Generation
↓
G1
↓
ZGC
GC는 계속 발전해 왔다.
발전 방향은
"더 적게 멈추는 것" 이다.
정리
ZGC는
Heap 크기와 관계없이 매우 짧은 Pause Time을 유지하는 것을 목표로 설계된 GC 다.
핵심 기술은
Colored Pointer
↓
Load Barrier
↓
Concurrent Relocation
이다.
덕분에 객체 이동과 참조 갱신을 대부분 애플리케이션 실행과 동시에 수행할 수 있다.
하지만 모든 서비스에 ZGC가 정답은 아니다.
대부분의 Spring Boot 기반 웹 서비스에서는 G1 GC가 여전히 가장 현실적인 선택이며,
ZGC는 초대형 Heap과 초저지연이 반드시 필요한 환경에서 진가를 발휘한다.
다음 글
다음 글에서는 문자열 처리 최적화(String Performance Optimization) 를 다룬다.
왜 String은 immutable로 설계되었는지, StringBuilder, StringBuffer, StringConcatFactory, 컴파일러 최적화, intern(), 문자열 풀(String Pool)까지 실제 JVM이 문자열을 어떻게 최적화하는지 깊이 있게 살펴본다.
'language > java' 카테고리의 다른 글
| G1 GC (Garbage First GC) (0) | 2026.06.29 |
|---|---|
| GC 튜닝 기초 (왜 대부분의 성능 문제는 GC보다 객체 생성 패턴에서 시작되는가) (0) | 2026.06.29 |
| Escape Analysis 재방문 (Scalar Replacement, Lock Elision) (0) | 2026.06.29 |
| 객체 생성 비용 줄이기 (Object Creation Cost) (0) | 2026.06.29 |
| Spring MVC 예외 처리 내부 구조 (0) | 2026.06.24 |
댓글