Java GC Type

Serial GC Parallel GC CMS GC G1 GC ZGC

Z Garbage Collector(ZGC)는 빠르고 효율적인 GC다.
ZGC는 메모리가 커지더라도 STW의 시간을 상수 값으로 가져가는 것을 목표로 설계되었다. 설계 철학에 맞게 GC 과정 중 1ms 이상의 STW는 없다고 한다. (문서마다 수 ms 라고 표현하기도 함)
그래서 빠른 반응이 중요한 프로그램에 적합하다. ZGC는 작은 메모리(몇백 MB)부터 아주 큰 메모리(16TB)까지 잘 작동하지만, 큰 메모리에서 사용이 권장된다.
ZGC는 JDK 11에서 experimental로 도입되었고, JDK 15에서 정식 기능으로 채택되고 JDK 21에서 개선됐다.

ZGC 사용법

ZGC는 application이 실행되는 동안 동시에 GC를 처리한다. 때문에 반응 속도에 미치는 영향을 최소화할 수 있다.
web application 같은 경우, 동시에 많은 요청을 처리해야 하기 때문에 ZGC의 빠른 반응과 확장성이 유리하다.
하지만 너무 작은 메모리(8MB 이하)에서는 사용하지 않는 것이 좋다.

사용해 보기

  • 기본적으로 G1 GC를 사용한다.
    • Throughput가 부족하다면 → Parallel GC로 전환.
    • STW가 너무 길다면 → ZGC로 전환.

ZGC cycle

ZGC는 몇 가지 STW을 가진다. Pause Mark Start, Pause Mark End, Pause Relocate Start 같은 작업이 있다.
이 STW는 1밀리초 이하로 매우 짧으며, 가비지 컬렉션 과정에서 데이터의 정확성과 일관성을 유지하기 위해 필요하다.

STW 목적 이유
Pause Mark Start 시작 지점(root references) 기록 마킹 시작 전에 일관성을 보장하기 위해
Pause Mark End 마킹 완료 및 참조 처리 모든 살아 있는 객체를 확인하기 위해
Pause Relocate Start 객체 이동 준비 이동 전 일관성을 유지하기 위해

zgc_cycle

Colored Pointers

ZGC는 포인터에 추가 정보를 넣어 객체를 추적한다. 이 정보를 사용하면 application을 멈추지 않고 객체를 관리할 수 있다.
객체가 살아있는지, 이동했는지를 이 포인터를 통해 확인할 수 있다.

zgc_colored_pointer

  • 첫 16비트: 예약 공간
  • 마지막 44비트: 객체 주소
  • 4비트 정보
    1. 마크 비트(Mark Bits): 객체가 살아 있는지 표시
    2. 리맵 비트(Remapped Bit): 객체가 이동했는지 표시
    3. 최종화 비트(Finalizable Bit): 최종 처리만 필요한 객체인지 표시

Heap Multi-Mapping

ZGC는 STW를 줄이기 위해 application이 실행 중일 때 객체를 heap 내에서 다른 물리적 위치로 이동시킨다.
객체가 이동한 위치로 접근하기 위한 방법으로 Heap Multi-Mapping을 사용한다.

multi mapping을 통해 객체의 물리적 위치를 가상 메모리에서 포인터의 각 color에 해당하는 view로 매핑한다.
이 매핑 방식 덕분에 load barrier가 마지막 동기화 이후 이동된 객체를 찾을 수 있다.

zgc_heap_multi_mapping

Load Barriers

ZGC에서 Load Barrier가 필요한 이유

ZGC는 GC 중에(압축 단계에서) 객체를 메모리 내에서 이동할 수 있다. 객체가 이동되면, 해당 객체를 참조하는 모든 참조를 새로운 위치로 업데이트해야 한다.
ZGC는 대부분의 작업을 application과 동시에 수행하기 때문에, Load Barrier는 application이 오래된 참조(잘못된 메모리 위치)를 사용하는 것을 방지한다.

Load Barrier는 어떻게 작동하는지?

  1. 프로그램이 객체에 대한 참조를 불러올 때(예: 필드 접근, 배열 접근 등), Load Barrier가 이 접근을 가로챈다.
  2. 해당 객체가 이동되었는지 확인한다. (즉, relocation set 에 속하는지 확인).
  3. 객체가 이동된 경우, Load Barriers는 참조를 새 위치로 업데이트한다.
  4. 이 과정은 application이 실행 중일 때 백그라운드에서 진행되며, ZGC가 객체를 이동하더라도 프로그램은 항상 올바른 위치의 객체에 접근할 수 있다.

ZGC Load Barrier의 장점

  • application STW 없음: Load Barrier 덕분에 ZGC는 객체를 이동하거나 메모리를 관리하면서도 application을 멈추지 않는다.
  • 일관성: Load Barrier는 프로그램이 항상 올바른 메모리 위치에서 객체를 읽도록 보장한다.
  • 동시성: ZGC는 객체를 이동하는 작업을 프로그램 실행과 동시에 안전하게 처리할 수 있어 GC로 인한 방해를 최소화한다.

Regions

zgc_region

ZGC는 G1 GC처럼 메모리를 영역으로 나눠서 관리한다. 하지만 ZGC의 영역은 ZPages로 정의되며, 더 유연한 구조를 갖는다.
작은 영역, 중간 영역, 큰 영역으로 나뉘며, 프로그램에서 필요한 메모리 크기에 따라 활성화된 영역의 개수가 동적으로 조절된다.

작은 영역

  • 크기: 2MB
  • 크기가 256KB 이하(작은 영역의 1/8 크기)인 객체는 작은 영역에 저장된다.

중간 영역

  • 크기: 32MB(힙 크기가 1GB 이상일 때)
  • 크기가 중간 영역 크기의 1/8 이하인 객체가 여기에 저장된다.

큰 영역

  • 크기: 객체 크기에 맞춰 2MB 단위로 조정
  • 크기가 13MB인 객체는 14MB 영역에 저장된다.
  • 중간 영역에 들어갈 수 없을 만큼 큰 객체는 큰 영역에 저장된다.

G1 GC와 비교

특징 G1 GC ZGC
목표 짧은 멈춤 + 높은 처리 속도 초저지연
힙 크기 중간 ~ 큰 메모리 (최대 32GB 이상) 아주 큰 메모리 (최대 16TB)
멈춤 시간 예측 가능하지만 힙 크기에 비례해 증가 항상 짧음 (10ms 미만)
압축(compaction) 멈춤 시간 중 처리하거나 단계적으로 진행 완전히 동시 처리
처리 속도 높음 약간 낮음

reference

  • https://wiki.openjdk.org/display/zgc/Main
  • https://dev.java/learn/jvm/tool/garbage-collection/zgc-overview/
  • https://dev.java/learn/jvm/tool/garbage-collection/zgc-deepdive/
  • https://cr.openjdk.org/~pliden/slides/ZGC-PLMeetup-2019.pdf