GC(Garbage Collection) (2)

김하영·2021년 3월 15일
0
post-custom-banner

- G1 GC

G1GC는 Garbage-First Garbage Collector를 줄여쓴 것이다.

G1은 이름을 보면 짐작할 수 있듯 쓰레기로 가득찬 heap 영역을 집중적으로 수집한다.
G1은 큰 메모리를 가진 멀티 프로세서 시스템에서 사용하기 위해 개발된 GC이다.
GC 일시 정지 시간을 최소화하면서 따로 설정을 하지 않아도 가능한 한 처리량(throughput)도 확보하는 것이 G1GC의 목표이다.

G1은 Java 9부터 디폴트 GC이다. G1은 실시간(real time) GC가 아니다.
일시 정지 시간을 최소화하긴 하지만 완전히 없애지는 못한다.
G1은 통계를 계산해가면서 GC 작업량을 조절한다.

다음 상황이라면 G1을 쓰면 도움이 된다.

Java heap의 50% 이상이 라이브 데이터.
시간이 흐르면서 객체 할당 비율과 프로모션 비율이 크게 달라진다.
GC가 너무 오래 걸린다(0.5 ~ 1초).

다른 GC와의 비교

Parallel GC
Parallel GC는 old gen의 공간에서만 재확보(reclaim)와 조각 모음(compaction)을 한다.
G1은 이런 작업을 더 짧은 GC 작업들로 분배하여 수행하여, 전체적인 처리량이 줄어드는 대신 일시 정지 시간을 크게 단축한다.

CMS
G1도 CMS처럼 old gen 영역을 동시에(concurrently) 작업한다.
CMS는 old gen의 조각 모음을 하지 않으므로 Full GC 시간이 길어지는 문제가 있다.

활성화하기

G1GC는 디폴트이므로 보통은 따로 옵션을 써서 활성화할 필요가 없다.
-XX:+UseG1GC 옵션을 쓰면 수동으로 활성화할 수 있다.

작동 방식

G1GC의 힙 레이아웃은 다른 generational collector와 좀 다르다.

G1GC는 전체 heap을 체스판처럼 여러 영역(region)으로 나누어 관리한다.

따라서 G1은 영역의 참조를 관리할 목적으로 remember set를 만들어 사용한다.
remember set은 total heap의 5% 미만 크기.

비어 있는 영역에만 새로운 객체가 들어간다.
쓰레기가 쌓여 꽉 찬 영역을 우선적으로 청소한다.
꽉 찬 영역에서 라이브 객체를 다른 영역으로 옮기고, 꽉 찬 영역은 깨끗하게 비운다.
이렇게 옮기는 과정이 조각 모음의 역할도 한다.

빨간색은 Eden으로 쓰이고 있는 영역을 의미한다.
빨간색 S는 Survivor. Eden이 꽉 차면 라이브 객체를 S로 옮기고 Eden은 비워버린다.
파란색은 old gen 처럼 쓰이고 있는 영역이다.
파란색 H는 한 영역보다 크기가 커서 여려 영역을 차지하고 있는 커다란 객체이다(Humongous Object).
G1GC는 일시 정지 시간을 줄이기 위해 병렬로 GC 작업을 한다.
각각의 스레드가 자신만의 영역을 잡고 작업하는 방식.

GC Cycle

G1은 두 페이즈를 번갈아 가며 GC 작업을 한다.

young-only 페이즈: old 객체를 새로운 공간으로 옮긴다.
space-reclamation 페이즈: 공간 회수.

old gen의 점유율이 threshold 값을 넘어서면 Young-only 페이즈로 전환된다.

Concurrent Start 단계: 도달할 수 있는 객체들에 마킹 작업을 한다.
Remark: 마킹을 끝내고, 쓰레기 영역을 해지한다.
Cleanup: Space-Reclamation 페이즈로 들어가야 할지 말지를 판단한다.
Space-Reclamation: young/old 가리지 않고 라이브 객체를 적절한 곳으로 대피시킨다(Evacuation). 작업 효율이 떨어지게 되면 이 페이즈는 끝나고, 다시 Young-only 페이즈로 전환된다.

만약 애플리케이션 메모리가 부족한 경우 G1GC는 다른 GC들처럼 Full GC를 수행한다.

G1GC의 마킹

G1GC는 SATB(Snapshot-At-The-Beginning) 알고리즘을 써서 마킹 작업을 한다.
SATB는 일시 정지가 일어난 시점 직후의 라이브 객체에만 마킹을 한다.
따라서 마킹하는 도중에 죽은 객체도 라이브 객체로 간주하는 보수적인 특징이 있다.
비효율적일 것 같지만 Remark 단계의 응답 시간(latency)이 다른 GC에 비해 더 빠른 경향이 있다.

- Concurrent Mark Sweep GC

CMS는 일시 정지 시간이 짧은 것을 선호하고 GC 작업과 프로세스 리소스를 공유할 수 있는 애플리케이션을 위한 GC이다. CMS GC는 Tri-color Marking Algorithm을 사용하기 때문에 GC와 함께 어플리케이션을 돌릴 수 있다.

아예 STW가 없는 건 아닌데 Parallel(Old)GC에 비하면 훨씬 짧다.
즉, 절반은 GC 돌리고, 절반은 어플리케이션 스레드를 돌리는 것이다.

CMS GC는 Old Generation 전용 GC이고, 해당 GC를 사용하면 자동적으로 Young Generation 전용으로 ParNewGC를 사용한다.

(Java 8에서 -XX:+UseConcMarkSweepGC -XX:-UseParNewGC 이 조합이 Deprecate 되었다.
그냥 -XX:+UseConcMarkSweepGC만 사용하면 된다.)

CMS GC의 장점은 아래와 같다.

어플리케이션 스레드가 오랫동안 멈추지 않는다. (짧게 짧게 쪼개서 멈춘다.)

CMS GC의 단점은 아래와 같다.

GC 풀 사이클 자체는 Parallel(Old)GC 보다 길다.
GC가 도는 도중에는 어플리케이션 스레드가 절반만 돌기 때문에 처리율이 감소한다.
Mark and Sweep 알고리듬에 비해 하는 일도 많고 복잡하다보니 메모리, CPU를 더 많이 쓴다.
CMS GC는 Old Generation의 메모리 Compaction을 수행하지 않으므로 단편화가 발생한다.

CMS GC는 어플리케이션 쓰레드와 같이 돌기 때문에 좀 복잡하게 동작한다.

  1. 초기 마킹(Initial Mark, STW 발생)

GC Root나 Young Generation에서 참조하는 객체를 회색으로 마킹한다.
이렇게 마킹해놓으면 마킹 단계에서 다른 메모리 영역은 신경쓰지 않고 하나의 GC 풀에만 신경쓰면 된다.

  1. 동시 마킹(Concurrent Mark)


마킹 쓰레드와 어플리케이션 쓰레드가 동시에 돌기 때문에 객체 참조 간에 변경 사항이 발생한다.

  1. 동시 사전 정리(Concurrent Preclean)

Old Generation 영역을 일정 공간으로 나누어 Card라 불리는 곳에 저장한다.
그리고 Initial Mark 이후에 참조 간에 변경이 생긴 곳을 Dirty Card라고 부른다.

사전 정리를 해야 Dirty Card에서 일반 Card가 될 수 있다.
이 phase의 목적은 5단계(재마크, Final Remark) 시에 STW 시간을 줄이기 위함이다.

  1. Abort 가능한 동시 사전 정리(Concurrent Abortable Precelan)

이 phase의 목적 역시 5단계(재마크, Final Remark) 시에 STW 시간을 줄이기 위함이다.
해당 phase는 이름부터 Abortable이 들어가기 때문에 아래와 같은 조건 의해 Abort 될 수 있다.
반복 횟수를 넘거나 정해진 시간을 초과하거나 사전 정리가 효과적으로 이루어졌거나 등 조건을 설정한다.
(JVM 옵션으로 설정 가능한 것들도 있다.)

  1. 재마크 (Final Remark, STW 발생)

Old Generation의 모든 live object를 마킹한다.

  1. 동시 쓸어담기 (Concurrent Sweep)

어플리케이션 스레드가 돌아감과 동시에 하얀색으로 마킹된 오브젝트를 전부 수거해간다.

  1. 동시 리셋 (Concurrent Reset)

어플리케이션 스레드가 돌아감과 동시에 마킹했던 내용들을 리셋한다.

CMS는 -XX:+UseConcMarkSweepGC 옵션으로 활성화할 수 있으며, JDK 9 부터는 사용되지 않는다.

profile
Back-end Developer
post-custom-banner

0개의 댓글