개발자는 가비지 수집기 선정 시 다음 항목을 고려해야합니다.
성능 엔지니어는 수집기 선정 시 다양한 트레이드 오프와 관심사를 면밀히 검토해야합니다.
핫스팟 병렬 수집기에서 STW 가비지 수집을 실행하려면 애플리케이션 스레드를 모두 중단시켜야합니다.
JVM은 사실 완전히 선제적인 멀티스레드 환경이 아님. OS 코어 기능처럼 JVM도 조정 작업이 필요.
애플리케이션 스레드마다 세이프포인트(안전점, safe point)라는 특별한 지점을 둡니다.
세이프포인트는 스레드의 내부 자료 구조가 보이는 지점이며, 이때 어떤 작업을 하기 위해 스레드는 잠시 중단될 수 있습니다.
JVM은 다음 두가지 규칙에 따라 세이프포인트를 처리합니다.
따라서 세이프포인트 요청을 받았을 때 그 지점에서 스레드가 제어권을 반납하게 만드는 코드가 VM 인터프리터 구현체 어디에 잇어야합니다. (세이브 포인터는 JIT컴파일러에의해 코드에 삽입된다.)
JVM이 전역을 세이프포인트 시간 플래그를 세팅합니다.
각 애플리케이션 스레드는 폴링(주기적으로 세이브 포인트 검사)을 하면서 이 플래그가 세팅됐는지 확인합니다.
애플리케이션 스레드는 일단 멈췄다가 다시 깨어날 때까지 대기합니다.
세이프포인트 시간 플래그를 세팅하면 모든 애플리케이션 스레드는 반드시 멈춰야합니다. 일반 애플리케이션 스레드는 이런 식으로 풀링
다음의 경우, 스레드가 꼭 세이프포인트 상태가 되는 것은 아닙니다.
삼색 마킹 알고리즘은 다음 순으로 진행됩니다.
CMS 수집기(동시 수집)는 중단 시간을 아주 짧게 하려고 설계된 테뉴어드(올드) 공간 전용 수집기 입니다. 보통 Parallel GC와 함께 사용합니다.
CMS는 중단 시간을 최소화하기 위해 애플리케이션 스레드 실행 중에 가급적 많은 일을 합니다. 따라서 일부 수행 단계는 좀 더 복잡합니다.
=( 카드 테이블을 이용해 변경자 스레드가 동시 마킹 단계 도중 영향을 끼친 마킹을 조정한다. )
=( young객체와 연결된경우 살려야 한다.)
카드 테이블
: Old 영역에는 512바이트의 덩어리(chunk)로 되어있는 card table이 존재한다.
card table에는 old 영역에 있는 객체가 young 영역의 객체를 참조할 때마다 정보가 표시된다. young영역의 GC를 실행할 때에는 old 영역에 있는 모든 객체의 참조를 확인하지 않고, 이 카드 테이블만 뒤져서 GC대상인지 식별한다.
=(쓰레기를 정리하는 작업을 실행한다.)
장단점
G1은 이름을 보면 짐작할 수 있듯 쓰레기로 가득찬 heap 영역을 집중적으로 수집한다.
G1은 큰 메모리를 가진 멀티 프로세서 시스템에서 사용하기 위해 개발된 GC이다.
GC 일시 정지 시간을 최소화하면서 따로 설정을 하지 않아도 가능한 한 처리량(throughput)도 확보하는 것이 G1GC의 목표이다.
가비지 우선은 병렬 수집기나 CMS와는 전혀 다른 스타일의 수집기입니다. 다음의 특징이 있습니다.
G1은 Java 9부터 디폴트 GC이다. G1은 실시간(real time) GC가 아니다.
일시 정지 시간을 최소화하긴 하지만 완전히 없애지는 못한다.
G1은 통계를 계산해가면서 GC 작업량을 조절한다.
Java heap의 50% 이상이 라이브 데이터.
시간이 흐르면서 객체 할당 비율과 프로모션 비율이 크게 달라진다.
GC가 너무 오래 걸린다(0.5 ~ 1초).
Parallel GC
Parallel GC는 old gen의 공간에서만 재확보(reclaim)와 조각 모음(compaction)을 한다.
G1은 이런 작업을 더 짧은 GC 작업들로 분배하여 수행하여, 전체적인 처리량이 줄어드는 대신 일시 정지 시간을 크게 단축한다.
CMS
G1도 CMS처럼 old gen 영역을 동시에(concurrently) 작업한다.
CMS는 old gen의 조각 모음을 하지 않으므로 Full GC 시간이 길어지는 문제가 있다.
G1 힙은 영역(리전)으로 구성됩니다.
영역 크기 : <힙 크기> / 2048
영역 개수 : <힙 크기> / <영역 크기>
G1수집기는 다음의 역할을 수행합니다.
G1 수집기는 워밍업을 하는 동안, GC 사이클이 한번 돌때마다 많은 '일반' 영역에서 가비지를 수집할 수 있는지 그 수치를 보관합니다.
G1 수집기는 TLAB 할당이나 서바이버 공간으로 방출, 테뉴어드 영역으로의 승격 등은 앞서 나온 다른 핫스팟 수집기와 대동소이하지만, 세대를 구성하는 영역이 연속되지 않는다는 단점이 있습니다.
G1 수집기에도 기억 세트(RSet, remembered set)라는 비슷한 장치로 영역을 추적합니다. RSet은 영역별로 하나씩, 외부에서 힙 영역 내부를 참조하는 레퍼런스를 관리하기 위한 장치입니다.
G1의 수집단계는 다음과 같습니다.
G1은 두 페이즈를 번갈아 가며 GC 작업을 한다.
young-only 페이즈: old 객체를 새로운 공간으로 옮긴다.
Young Only (Garbage Collection) Phase는 Minor GC만 수행하다가 XX:InitiatingHeapOccupancyPercent(Old Generation 비율)에 지정된 값을 초과하는 순간 Major GC가 수행된다. 그림에서 Old gen occupancy exceeds threshold 부분을 말하는 것이다.
Major GC의 첫 단계는 Initial Mark이며 Minor GC와 동시에 수행되며 둘 다 STW를 수반하므로 다른 파란 원보다 크기가 크다. 그 이후에 애플리케이션 스레드, Minor GC, Concurrent Mark가 동시에 수행되는데 Remark가 수행되는 순간 다른 작업은 멈추게 된다. 그래서 Remark에 해당하는 주황색이 원이 큰 것을 알 수 있다. 그 이후에 자잘하게 Minor GC가 수행되다가 Major GC의 Cleanup이 발생한다.
space-reclamation 페이즈: 공간 회수.
Young Only Phase가 끝나고 Space Reclamation(공간 회수) Phase가 시작된다. 해당 Phase에서는 Mixed GC가 수행되는데 Mark 단계가 없어서 STW 빈도가 Young Only Phase에 비해 줄어든 것을 알 수 있다. Space Reclamation Phase가 끝나면 다시 Young Only Phase로 돌아가서 Minor GC를 수행한다.
old gen의 점유율이 threshold 값을 넘어서면 Young-only 페이즈로 전환된다.
Concurrent Start 단계: 도달할 수 있는 객체들에 마킹 작업을 한다.
Remark: 마킹을 끝내고, 쓰레기 영역을 해지한다.
Cleanup: Space-Reclamation 페이즈로 들어가야 할지 말지를 판단한다.
Space-Reclamation: young/old 가리지 않고 라이브 객체를 적절한 곳으로 대피시킨다(Evacuation). 작업 효율이 떨어지게 되면 이 페이즈는 끝나고, 다시 Young-only 페이즈로 전환된다.
만약 애플리케이션 메모리가 부족한 경우 G1GC는 다른 GC들처럼 Full GC를 수행한다.
G1GC는 SATB(Snapshot-At-The-Beginning) 알고리즘을 써서 마킹 작업을 한다.
SATB는 일시 정지가 일어난 시점 직후의 라이브 객체에만 마킹을 한다.
따라서 마킹하는 도중에 죽은 객체도 라이브 객체로 간주하는 보수적인 특징이 있다.
비효율적일 것 같지만 Remark 단계의 응답 시간(latency)이 다른 GC에 비해 더 빠른 경향이 있다.
+XX:UseG1GC
G1의 주목표는 중단 시간 단축입니다. 따라서 최대 중단 시간을 개발자가 선정할 수 있는데, 값을 너무 잡게 잡으면 GC 서브시스템이 목표에 맞추지 못할 것입니다.
결론적으로는 8u40부터 사용하는 것이 지향되며, 저지연 워크로드에서는 아직 G1은 CMS 중단 시간보다 긴 편이라 아직까지는 긴편입니다. 다만 여전히 개선되고 있습니다.
가바지 수집기는 플랫폼의 강력한 장점이지만, 이를 잘알고 수집기 별로 고려해야하는 트레이드 오프를 아는 것이 중요합니다.
https://plumbr.io/handbook/garbage-collection-algorithms-implementations/concurrent-mark-and-sweep/cms-full-gc
https://velog.io/@ha0kim/GCGarbage-Collection-2
https://huisam.tistory.com/entry/jvmgc
https://steady-coding.tistory.com/590