같은 문제, 다른 목표

두 수집기 모두 큰 힙을 다루지만 겨냥하는 지점이 다르다.

G1 — 쓰레기가 많은 구역부터

G1은 힙을 크기가 같은 region 수천 개로 나눈다. Eden, Survivor, Old를 연속된 큰 덩어리가 아니라 region 집합으로 관리한다.

수집할 때는 전부 훑지 않는다. 각 region의 쓰레기 비율을 추적해두고 회수량 대비 비용이 좋은 region부터 고른다. 이름 그대로 garbage first다.

-XX:MaxGCPauseMillis로 목표 정지 시간을 주면, G1은 그 예산 안에서 처리할 수 있는 만큼만 region을 골라 수집한다. 목표를 너무 낮게 잡으면 한 번에 조금씩만 회수하므로 GC가 잦아지고 처리량이 떨어진다.

ZGC — 애플리케이션을 멈추지 않고 옮긴다

객체를 이동시키는 작업이 정지의 주범이다. 참조를 전부 새 주소로 고쳐야 하기 때문이다.

ZGC는 포인터 자체에 표시(colored pointer)를 심고, 참조를 읽는 순간 배리어가 개입해 필요하면 그 자리에서 새 주소로 고친다. 그래서 이동과 참조 갱신을 애플리케이션과 동시에 진행할 수 있다.

정지가 필요한 구간은 루트 스캔 정도로 좁혀지고, 이 비용은 힙 크기에 비례하지 않는다. 힙이 수십 GB든 수 TB든 정지 시간이 비슷한 이유다.

무엇을 내주었나

공짜는 아니다. 참조를 읽을 때마다 배리어가 끼어들므로 처리량이 다소 손해를 본다. 메모리도 더 쓴다.

어떻게 고를까

정리

수집기 선택은 “무엇이 더 좋은가”가 아니라 정지 시간과 처리량 중 무엇을 먼저 지킬 것인가의 문제다.