| 비교 항목 | G1GC (Garbage-First) | ZGC (Z Garbage Collector) |
|---|---|---|
| 주요 목표 | 처리량(Throughput)과 지연 시간의 균형 | 극단적으로 낮은 지연 시간 (STW < 1ms) |
| STW 발생 시점 | 객체 마킹 및 대피(Evacuation, 객체 복사) 시 | 매우 짧은 Root Scan 및 일부 동기화 시점 |
| 메모리 구조 | 고정 크기의 Region (Eden, Survivor, Old 동적 할당) | 다양한 크기(Small, Medium, Large)의 Page |
| 핵심 기술 | Remembered Set, SATB | Colored Pointers, Load Barrier |
| 추천 힙 크기 | 4GB ~ 수십 GB | 수십 GB ~ 16TB (대용량 힙에 최적화) |
기존의 CMS나 Parallel GC는 Young/Old 영역이 물리적으로 통짜로 나뉘어 있어, 힙이 커질수록 전체를 스캔하고 청소하는 시간이 기하급수적으로 늘어났습니다.
G1GC는 이를 해결하기 위해 힙을 수천 개의 Region(구역)으로 쪼갰습니다. 그리고 "쓰레기(Garbage)가 가장 많은 구역부터 먼저(First) 치운다"는 전략을 씁니다. 전체를 다 치우는 대신, 사용자가 설정한 목표 정지 시간(-XX:MaxGCPauseMillis) 내에 치울 수 있는 만큼의 Region만 골라 담아 수집(Evacuation)하기 때문에 힙이 커져도 STW를 일정 수준 제어할 수 있습니다.
G1GC도 마킹은 애플리케이션 스레드와 동시에(Concurrent) 하지만, 살아남은 객체를 다른 Region으로 복사(Evacuation)할 때는 애플리케이션을 멈춥니다(STW). 옮기는 도중에 애플리케이션 스레드가 옛날 주소를 참조하면 데이터가 꼬이기 때문입니다. 따라서 G1GC는 살아있는 객체가 많을수록 STW가 길어집니다.
반면, ZGC는 객체 이동(Relocation)까지도 애플리케이션 스레드와 동시에(Concurrent) 해버립니다. 힙이 10GB든 16TB든 STW가 1ms 미만으로 유지되는 비결이 바로 이것입니다. 이를 가능하게 하는 치트키가 바로 Colored Pointers와 Load Barrier입니다.
세상에 공짜는 없습니다. ZGC가 압도적인 Latency(낮은 STW)를 얻은 대신 포기한 것은 Throughput(처리량), 즉 CPU 연산 효율입니다.
💡 최신 트렌드 (Java 21+ Generational ZGC):
과거의 ZGC는 세대 구분이 없어 힙 전체를 매번 검사해야 하므로 처리량이 많이 떨어졌습니다. 하지만 Java 21부터 Generational ZGC가 도입되면서 "대부분의 객체는 금방 죽는다"는 가설을 활용해 Young/Old 영역을 나누어 관리하기 시작했습니다. 이로 인해 ZGC의 약점이었던 Throughput이 G1GC 수준으로 크게 개선되었습니다.
아닙니다. 만약 서비스가 CPU 사용량이 이미 80~90%를 치솟는 CPU Bound 서비스라면, ZGC로 전환 시 Load Barrier 오버헤드 때문에 CPU가 버티지 못하고 서비스가 터질 수 있습니다. 또한, 객체 생성 속도가 너무 빠른 서비스(High Allocation Rate) 환경에서 ZGC를 쓰면, GC가 메모리를 비우는 속도보다 객체가 생성되는 속도가 더 빨라져 애플리케이션이 일시 정지하는 Allocation Stall 현상이 발생해 오히려 큰 지연이 생길 수 있습니다.