본문 바로가기

Java/JVM

[JVM] GC 알고리즘 종류

이번 장에서는 GC 알고리즘의 종류와 작동 방식, 선정 기준에 대해서 알아본다.


GC 알고리즘은 크게 Non Concurrent Collectors와 Concurrent Collectors다.
Non Concurrent Collectors에는 아래의 Collector들이 있다.

  • Serial Collector
  • Parallel Collector
  • Parallel Old Collector

Concurrent Collectors에는 아래의 Collector들이 있다.

  • Concurrent Mark Sweep Collector (CMS)
  • Garbage-First Collector (G1GC)
  • Z Collector (ZGC)

이번 진행하는 프로젝트에 맞는 Collector를 선정하기 위해 벤치마크를 진행하고 많은 문서들을 읽어 본 결과
좋고 나쁜 Collector는 없다. 다만 사용하는 곳이 다를 뿐이다.


GC 선정 기준

Collector의 작동 원리를 알아보기 전에 간략하게 빠르게 Collector를 선정하는 방법에 대해서 알아본다.
긴 글이 부담스러운 사람은 아래의 짧은 선정 기준만 읽어보고 선정해도 큰 프로젝트가 아니라면 괜찮을 듯 하다.
아래의 선정기준은 Oracle 공식문서를 참고하여 작성하였다.

  1. 성능상에 문제가 없다면 JVM이 선정해주는 Collector를 사용한다.
  2. 어플리케이션이 작은 데이터를 다루거나 싱글 코어 환경에서 작동하는 경우 Serial GC를 사용한다.
  3. 어플리케이션의 전체 처리량이 중요하고 Stop the world 시간이 1초 이상이어도 괜찮은 경우 Parallel GC를 사용한다.
  4. 응답 시간이 전체 처리량보다 중요하며 Stop the world 시간이 1초 이하여야하는 경우 Concurrent Collectors에서 선정하도록 한다.
    • java 15미만의 경우 ZGC는 UnlockExperimentalVMOptions을 활성해주어야하는 실험적 GC이기 때문에
      G1GC와 CMS를 비교하여 벤치마크를 진행해보고 자신의 서비스에 맞는 GC를 선정한다.
    • java 11 버전에서 G1GC는 공식적인 GC 알고리즘으로 적용되었으며 기본 GC로 사용된다.
    • java 8 버전의 경우 G1GC가 공식적인 GC 알고리즘으로 적용되기 전이므로 CMS를 사용한다.

정말 간략하게 정리해보았다.
시간이 없다면 위의 기준으로 Collector를 선택해도 무관하겠으나 정확히 자신의 서비스에 맞는 Collector를 선정하려면 실제 자신의 서버 환경에서 Collector를 변경해가며 벤치마크를 진행해보고 전체적인 성능과 Resource 사용량을 확인해보는 것이 좋다. (GC를 변경해가며 벤치마크를 진행하는 것은 추후에 따로 작성하도록 한다.)


GC 종류

모든 Garbage Collecting은 아래의 2가지 단계를 따르게 된다.

  1. Stop The World
    JVM이 Major GC를 수행하기 위해 어플리케이션의 GC를 수행하는 Thread이외의 모든 Thread를 멈추는 상황이다.
    GC의 성능 개선을 위해 튜닝은 보통 stop the world시간을 줄이는 작업을 하는 것으로 볼 수 있다.
    또한 이러한 문제를 해결하기 위해 JVM에서도 다양한 실행 옵션을 제공한다.
  2. Mark and Sweep
    • Mark: 사용되는 메모리와 사용되지 않는 메모리를 식별하는 작업
    • Sweep: Mark 단계에서 사용되지 않음으로 식별된 메모리를 해제하는 작업

stop the world를 통해서 모든 thread들이 중단되면 JVM은 stack의 모든 변수 또는 reachable 객체를 스캔하면서
어떤 객체를 참조하고 있는지 탐색하면서 사용되고 있는 메모리를 식별한다. 이러한 작업을 Mark라고 한다.
Mark가 되지 않는 객체들은 모두 메모리에서 제거되는데 이러한 과정을 Sweep라고 한다.

기본적인 동작방식을 알아보았으니 하나씩 알아보도록 한다.

  1. Serial GC
  • 싱글 스레드로 모든 종류의 가비지 컬렉션을 수행한다 (Context Switching으로 인한 Overhead를 줄일 수 있다).
  • 멀티 코어 환경에서도 리소스를 제대로 활용 할 수 없으므로 적은 메모리와 CPU 코어의 수가 적을 때 적합한 방식.
  • Young Generation Collection 알고리즘: Serial
  • Old Generation Collection 알고리즘: Serial Mark Sweep Compact
  • Mark Sweep Compact 알고리즘에서 Compact는 Heap 영역을 정리하기 위한 단계다.
    유효한 객체들이 연속으로 쌓이도록 Heap 영역의 앞 부분부터 채워서 객체가 존재하는 부분과 존재하지 않는 부분으로 나누는 과정이다.
  1. Parallel GC
  • 알고리즘은 Serial GC와 동일하다.
  • Serial GC는 Major GC와 Minor GC를 처리하는 Thread가 하나지만 Parallel GC의 경우 복수의 Thread가 GC를 진행한다.
  • 충분한 메모리와 CPU의 코어를 갖춘 경우 유리한 GC의 종류
  • Throughput GC라고도 부른다.
  • Young Generation Collection 알고리즘: Parallel Scavenge
  • Old Generation Collection 알고리즘: Serial Mark Sweep Compact
  • 아래의 그림은 Serial GC와 Parallel GC를 비교한 그림이다.

  1. Parallel Old GC
  • Parallel GC의 경우 Major GC, Minor GC 모두 Collection을 병렬로 수행한다.
  • Parallel Old GC의 경우 Minor GC는 병렬로 처리하지만 Major GC의 경우 싱글 Thread만으로 처리한다.

Concurrent Collectors의 특징

Concurrent Collectors(이하 Concurrent)들에 대해서 알아보기 전에 Concurrency의 오버헤드와 탄생한 이유를 간략하게 알아본다.
Concurrent의 탄생 목적은 stop the world 시간을 줄이는데 있다.
즉 어플리케이션이 구동하는 것과 Major GC를 Concurrency(동시에) 진행한다는 의미가 된다. 이러한 경우 Major GC가 발생하더라도 모든 thread가 중단되는 현상이 발생하지 않으며 어플리케이션이 중단되는 현상도 발생하지 않는다.
단, 어플리케이션이 동작하는 동시에 GC가 발생할 것이므로 Resource사용량이 증가하게 되고 어플리케이션 전반적인 성능이 하락하게 된다.

  1. Concurrent Mark Sweep (CMS)
  • Young Generation Collection 알고리즘: Parallel
  • Old Generation Collection 알고리즘: Concurrent Mark Sweep
  • Serial GC와 비교하였을 때 아래의 그림과 같은 차이점이 있다.

  • Concurrent Mark Sweep이라는 알고리즘이 등장하였는데 아래와 같은 단계를 진행한다.
    1. Initial Mark
      • 어플리케이션 일시 정지.
        • GC에서 싱글 Thread를 사용.
          어플리케이션의 Root set과 직접적으로 관계가 있는 객체만 Mark한다.
    2. Marking / Pre cleaning
      • GC Thread는 GC 작업을 진행하고 Working Thread는 어플리케이션 작업을 한다.
      • GC에서 싱글 Thread를 사용.
        Initial Mark 단계에서 체크한 객체가 바라보고 있는 객체들을 추적해 살아 있는지 Mark한다.
    3. Remark
      • 어플리케이션 일시 정지
      • GC에서 멀티 Threads 사용.
      • Mark한 객체를 다시 추적해서 살아있는지 확인한다.
    4. Sweeping
      • 어플리케이션은 멈추지 않고 작업을 계속한다.
      • GC는 싱글 Thread를 사용.
      • Sweep: 살아있는 객체를 제외한 죽은 객체를 메모리에서 해제한다.
      • Serial GC와 Parallel GC와 다르게 Compaction을 하지 않기 때문에 단편화가 발생한다.
  1. Garbage-First GC (G1GC)
  • Eden, Survivor, Old 영역이 고정된 하나의 영역이 아니라 Region이라는 특정한 크기로 나뉘어져있다.
    Region의 상태에 따라 Region의 역할이 동적으로 변동하게 된다.
  • Young Generation Collection 알고리즘: Snapshot-At-The-Beginning(SATB)
  • Old Generation Collection 알고리즘: Snapshot-At-The-Beginning(SATB)
  • G1이라는 이름은 Garbage로 가득 찬 Region부터 Collection을 시작한다는 의미.
    Garbage로 가득 찬 Region이 발견되면 즉시 Collection을 시작한다.
  • G1GC Heap 구조를 그림으로 그리면 아래와 같은 그림이 된다.

Humonogous: Region 크기의 50%를 초과하는 큰 객체를 저장하기 위한 공간
Available/Unused: 사용되지 않은 Region

G1GC의 GC 과정은 아래의 그림과 같다.

- Initial Mark: Old Region에 존재하는 객체들이 참조하는 Survivor Region을 찾는다. (stop the world)
- Root Region Scan: Initial Mark 단계에서 찾은 Survivor Region에서 실제 객체를 찾는다.
- Concurrent Mark: 전체 Heap의 scan 작업을 실시하고 GC 대상 객체가 발견되지 않은 Region은 이후 단계를 제외한다.
- Remark: 최종적으로 GC 대상에서 제외할 객체를 식별한다. (stop the world)
- Cleanup: 살아있는 객체가 가장 적은 Region에서 사용하지 않는 객체를 제거한다. (stop the world)
- Copy: GC의 대상이었던 Region 중에서 Cleanup 과정을 통해 완전히 비워지지 않은 Region의 살아남은 객체를 새로운 Available/Unused Region에 복사하여 Compaction을 수행한다.
- 살아있는 객체가 아주 적은 Old 영역에 대해 [GC pause(mixed)]를 로그로 표기하고 Young GC가 이루어질 때 수집되도록 한다.
  1. ZGC
  • 8MB ~ 16TB에서 효율적으로 GC하기 위한 알고리즘
  • 적은 메모리나 큰 메모리에서 stop the world를 최대한 적게 (10ms 이하)하기 위해 탄생.
  • Marking 단계에서만 stop the world가 발생.

ZGC는 ZPages를 사용하는데 이는 G1GC의 Region과 비슷한 영역의 개념을 사용한다.
차이점은 G1GC의 Region은 역할은 동적이지만 크기는 고정이었다.
하지만 ZGC의 ZPages는 크기가 2의 배수로 동적으로 생성 및 삭제가 가능하다.

ZGC의 Heap 구조를 그려보면 아래와 같다.

ZGC를 처리하는 프로세스와 핵심 알고리즘은 아직 명확하게 이해가 되지않았다.
이미지와 참고 문서를 정리해두고 추후에 정리하도록 한다.

GC Marking Process


https://sauravomar01.medium.com/zgc-garbage-collector-low-latency-and-scalable-73cd3d80f416

Relocation


https://malloc.se/blog/zgc-jdk16

Multi Mapping


https://www.baeldung.com/jvm-zgc-garbage-collector

Colored Pointers


https://www.baeldung.com/jvm-zgc-garbage-collector
https://sauravomar01.medium.com/zgc-garbage-collector-low-latency-and-scalable-73cd3d80f416

Load Barrier


https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/

https://d2.naver.com/helloworld/1329
https://d2.naver.com/helloworld/37111
https://johngrib.github.io/wiki/jvm-memory/#gc-%EC%84%A0%ED%83%9D-%EA%B0%80%EC%9D%B4%EB%93%9C%EB%9D%BC%EC%9D%B8-%EC%9A%94%EC%95%BD
https://malloc.se/blog/zgc-jdk16
https://www.geeksforgeeks.org/z-garbage-collector-in-java/
https://sauravomar01.medium.com/zgc-garbage-collector-low-latency-and-scalable-73cd3d80f416
https://www.baeldung.com/jvm-zgc-garbage-collector

'Java > JVM' 카테고리의 다른 글

[JVM] GC 벤치마크 결과  (0) 2022.01.23
[JVM] GC 벤치마크 분석 포인트  (0) 2022.01.23
[JVM] GC 벤치마크 개요  (0) 2021.12.23
[JVM] Heap & GC  (0) 2021.12.02
[JVM] JVM 이란?  (0) 2021.12.02