GC가 작동하는 방식을 알아보기전, JVM의 메모리 구조가 어떻게 되는지 알아보자.

OutOfMemoryError: PermGen space 에러가 발생할 수 있다.OutofMemoryError 발생을 크게 줄일 수 있다.jdk 1.1 이전에 사용하던 오래된 gc

사용중인 객체를 mark 하고 mark되지 않은 객체를 sweep을 통해 제거한다.
그림에서도 보이듯 sweep한후 메모리는 실제로 11칸의 여유 공간이 있지만 실질적으로 연속되게 저장 가능한 최대 공간은 3칸으로 그 이상의 객체가 생성되면 에러를 뱉게 된다. 이 문제를 메모리 단편화라고 한다.
가장 기본적인 방식의 GC
jdk 1.2에 추가 되었다.

Heap에서 아직 사용하는 객체를 Mark한 후 Mark되지 않은 객체를 Sweep하여 삭제한다.
그 후 Compaction을 통해 메모리 단편화를 해결한다.
Serial GC에서는 Generational GC라는 개념이 도입되어 효율적인 GC 처리가 가능하게 되었고, Generational GC는 현재까지 사용되는 GC 수행 개념이다.
Generational GC는 대부분의 객체가 많이 사용되기전 버려진다는 점을 활용해 적게 사용된 객체, 많이 사용되는 객체를 분류하여 관리하는 방식이다. 이를 통해 일반적인 상황에서는 Minor GC로 stop the world 시간을 최소화 한다.

Generational GC에서는 Heap을 Young Genetion, Old Generation으로 나눈다. Young Generation에서는 생성된지 얼마 되지 않은 객체들이 저장되며 Old Generation에서는 오랬동안 생성된 객체들이 저장된다.
Young Generation은 다시 Eden과 두개의 Survivor 공간으로 나뉜다.
Survivor가 두개인 이유는 Compaction 작업을 쉽게 하기 하도록 위함이다.
Survivor는 항상 하나는 비어있는 상태, 다른 하나는 메모리 단편화 없이 객체들이 저장된 상태를 유지한다.
1. Eden에서는 처음 생성된 객체가 저장된다.
Minor GC가 한번 실행되면 Survivor중 하나로 이동한다.
GC가 실행될때 비어있지 않은 Survivor 한쪽에서 Mark, Sweep을 한후 남은 객체들을 비어있는 Survivor로 Copy 함으로써 Compaction을 수행한다.
이렇게 Minor GC가 실행될 때마다 객체들은 survivor들을 왔다갔다하며 15번 Minor GC를 겪은 후 Old Generation으로 이동된다.
Old Generation에서 수행되는 GC를 Major GC라고 하는데, 대부분의 객체는 Young Generation에서 사라지기 때문에 Major GC는 Old Generation의 메모리 공간이 부족할 때만 발생한다. Major GC는 모든 객체를 Scan, Mark, Sweep, Compaction 한다. Survivor 처럼 Copy를 통한 Compaction이 불가능 하기 때문에 Compatcion이 복잡하다. 이러한 방식을 Full GC라고 부르며, 과정에서 보이듯 Full GC는 느리고 시간이 오래 걸린다.
Parallel GC는 Serial GC에 멀티 스레딩을 추가한 GC이다.
멀티스레딩을 통해 GC의 수행시간을 줄였고, 그에 따라 stop the world의 시간이 줄어들었다.
stop the world의 시간을 최소화 하기 위해 고안된 방법이다.
Minor GC는 Parallel GC와 같이 Eden, Survivor를 사용하여 실행된다.
CMS의 Major GC에서는 Concurrent Mark, Concurrent Sweep을 사용하는데, 애플리케이션의 작업을 멈추지 않고 GC 작업과 병렬로 수행 할 수 있다.
물론 stop the world가 아예 사라진 것은 아니다.
CMS에서 마킹은 Initial Mark - Concurrent Mark - Final Mark 순서대로 실행되는데, Inital Mark에서는 GC에서 애플리케이션에서 직접적으로 참조하는 Root 객체들을 마킹한다. Concurrent Mark는 애플리케이션이 실행되는 동시에 Root 객체들이 참조중인 모든 객체들을 Mark한다. Final Mark에서는 Concurrent Mark에서 놓친 살아있는 객체들을 Mark한다.
이름에서 처럼 Concurrent Mark에서는 stop the world가 발생하지 않지만 Initial Mark와 Final Mark에서는 짧은 시간의 stop the world가 발생한다.
CMS GC에서는 sweep 후에 compaction을 진행하지 않아 stop the world가 inital mark, final mark에서만 발생하는 것이 특징이다. 하지만 메모리 단편화로 인해 old generation에 객체를 추가할 수 없는 상태가 되면 serial GC, parallel GC에서 쓰이던 Full GC를 실행하여 이를 해결해야한다. 이때 stop the world가 비교적 긴시간 발생하게 된다.
현재 Java의 기본 GC로 채택되어 있는 GC이다.
기존처럼 heap을 eden, survivor, old로 잘라서 관리하지 않고, heap 메모리 전체를 Region이라는 단위로 관리한다. 2048개의 Region을 가질 수 있고, 각각의 Region의 크기는 1MB~32MB 사이이다. 각각의 Region에 역할을 부여하여 기존 Generational GC를 구현한다.

G1 GC에서 새로 추가된 Humongous는 Region크기의 절반이 넘는 크기를 가진 객체를 말하며 young generation을 거치지 않고 Old 구역에 저장된다. 경우에 따라 여러개의 Old 구역에 걸쳐 저장될 수 있다.
Available/Unused는 비어있는, 사용가능한 Region이다.
+편의상 Empty로 지칭하겠습니다..
G1 GC에서는 효율적인 처리를 위해 Minor GC, Full GC 뿐만 아니라 young generation을 old generation과 같이 처리하는 Mixed GC도 사용된다.
IHOP(Initial Heap Occupancy Percent)은 Old 구역의 메모리 사용률에 제한을 나타낸다. 만약 Old 구역의 사용률이 IHOP 보다 높다면 Minor GC 대신 Mixed GC가 실행된다.
Mixed GC는 MaxGCPauseMillis를 넘지 않는 선에서 Young generation과 일부 Old 구역을 포함하여 실행된다. Old 구역중 Mixed GC를 수행할 Old 구역들은 구역의 Garbage 비율에 따라 선정되며 Garbage 비율을 알기 위해서 마킹 과정엔 Old 구역 전체가 포함된다. Garbage 비율이 일정 값 이상이면 순차적으로 MaxGCPauseMillis(gc로 인한 stop the world의 최대 시간)을 넘지 않는 선에서 Mixed GC 대상에 포함시킨다.
+ 이렇게 Mixed GC에서 GC를 수행할 대상들을 담은 Set을 CSet(Collection Set)이라고 합니다.
Mixed GC는 Old 구역의 사용률이 IHOP를 넘으면 Concurrent Mark Cycle을 통해 살아있는 객체를 마크한다. Concurrent Mark Cycle이 끝나면 위 설명처럼 일부 Old 구역을 CSet에 추가한다. CSet이 완성되면 마크된 객체를 빈 region으로 copy하고, 원래 region은 비운다.
G1 GC에서는 Mixed GC에도 불구하고 Heap 공간이 부족해지면 Full GC가 실행된다. Full GC는 싱글스레드로 동작하기 때문에 stop the world 시간이 길어진다. Adaptive IHOP 옵션이 켜져있다면 jvm에서는 자동으로 IHOP의 값을 조정하여 Full GC의 발생 빈도를 줄인다.
stop the world의 시간을 최소화 하기 위해 고안된 방식이다.
다른 GC들과 다르게 Heap을 young & old으로 구분짓지 않고 하나로 관리한다. Region은 여전히 존재하지만 64GB의 heap을 지원했던 G1과 달리 최대 2TB의 Heap을 지원한다.
각각의 Region은 독립적으로 GC가 작동되며 작동 방식은 다음과 같다
이렇게 대부분의 GC 과정을 애플리케이션과 함께 진행하여 stop the world의 시간을 최소화 하는것이 Shenandoah GC의 특징이다. 그럼에도 불구하고 cpu 자원을 많이 사용하여 아직 기본 GC로 채택되진 못했다.
개발용으로 사용되는 GC 옵션중 하나로, 메모리 할당은 진행하지만
Garbage 수집은 하지 않는다.
당연히 실행 속도는 빨라지겠지만, 금방 jvm에서 메모리가 부족하여 멈추게 될것이다.

지금까지 설명했던 각 GC별 특징이 위 사진 하나로 정리된다.
JVM 메모리 구조와 GC 작동 방식에 대해 알아보았는데, 사실 제대로 이해했는지는 아직 의문이다. 나중에 다시 글을 고쳐가며 알아가야 할 것 같다.