가비지 컬렉션은 메모리를 정리하는 과정입니다.
그렇기 때문에 일반적으로 메모리의 사용을 중단한 채로 진행이 되어야 합니다.
JVM은 GC를 실행하기 위해 애플리케이션의 실행을 멈추는 Stop-the-World를 먼저 실행하게 됩니다.
Stop-the-World를 실행하면 GC를 실행하는 쓰레드를 제외한 모든 쓰레드가 작업을 멈춥니다.
그리고 GC가 끝나면 다시 작업을 재개합니다.
GC의 작업은 Young 영역에 대한 Minor GC와 Old영역에 대한 Major GC로 구분됩니다.
Young 영역은 새롭게 생성한 객체들이 위치하는 영역입니다.
대부분의 객체는 금방 접근 불가능한 상태가 되기 때문에, 많은 객체가 Young영역에 생성되었다가 사라집니다.
또한 Young영역은 1개의 Eden 영역과 2개의 Survivor 영역으로 구성됩니다.
Old 영역은 Young 영역에서 계속 살아남은 객체가 복사되는 영역입니다.
Young 영역보다 크게 할당되며, 더 적은 GC가 발생합니다.
Minor GC가 작용하는 순서는 먼저 새로운 객체가 제일 먼저 저장되는 Eden영역에서 동작됩니다.
이 때 살아남은 객체는 첫번째 Survivor영역으로 이동하게 되고 이 과정이 반복되어 첫번째 Survivor영역이 가득 차게 되면 해당 첫번째 Survivor영역에 대해 GC가 동작하고 살아남은 객체는 두번째 Survivor영역으로 이동하고 첫번째 Survivor영역을 모두 비웁니다.
이 과정을 번갈아 가면서 한 쪽 Survivor영역은 무조건 비워 두는 상태가 됩니다. 이 때, 특정 횟수만큼 살아남은 객체는 Old영역으로 이동됩니다.
단 여기서 Servior 영역을 거치지 않고 바로 Old 영역으로 이동하는 경우가 있습니다. 바로 객체의 크기가 Servior 영역의 크기보다 큰 경우입니다.
Major GC는 이 때 Old영역이 가득차서 더 이상 이동이 불가능할 때 동작합니다.
JDK 7기준으로 존재하는 5가지 방식이 있습니다.
이 중에서 운영서버에서 절대 사용하면 안 되는 방식이 Serial GC입니다.
이를 사용하면 애플리케이션의 성능이 많이 떨어지게 됩니다.
하나의 CPU로 Young영역과 Old영역을 연속적으로 처리합니다.
컬렉션이 수행될 때 애플리케이션이 정지됩니다.
Young영역에서의 Minor GC는 앞서 말한 방식을 사용합니다.
Old영역의 GC는 mark-sweep-compact라는 알고리즘을 사용합니다.
이 알고리즘의 첫 단계는 Old 영역에 살아 있는 객체를 식별하는 것입니다.
그 다음에는 힙의 앞부분부터 확인하여 살아 있는 것만 남깁니다. (Sweep)
마지막 단계에서는 각 객체들이 연속되게 쌓이도록 힙의 가장 앞부분부터 채워서 객체가 존재하는 부분과 객체가 없는 부분으로 나눕니다. (Compaction)
이러한 Serial GC는 적은 메모리와 CPU 코어 개수가 적을 때 적합한 방식입니다.
Parallel GC는 Serial GC와 기본적인 알고리즘은 같지만 Serial GC는 GC를 처리하는 스레드가 하나인 것에 비해, Parallel GC는 GC를 처리하는 쓰레드가 여러 개입니다.
그렇기 때문에 Serial GC보다 빠르게 객체를 처리할 수 있습니다.
Parallel GC는 메모리가 충분하고 코어의 개수가 많을 때 유리합니다.
Parallel GC는 Throughput GC라고도 부릅니다.
Parallel Old GC는 JDK5 update 6부터 제공한 GC방식입니다.
Parallel GC와 비교하여 Old 영역의 GC알고리즘만 다릅니다.
이 방식은 Mark-Summary-Compaction 단계를 거칩니다.
Summary 단계는 앞서 GC를 수행한 영역에 대해서 별도로 살아 있는 객체를 식별한다는 점에서 Mark-Sweep-Compaction 알고리즘의 Sweep 단계와 다르며, 약간 더 복잡한 단계를 거칩니다.
CMS GC는 초기 initial mark 단계에 클래스로더에서 가장 가까운 객체 중 살아있는 객체만 찾습니다.
아주 짧은 시간 동안 이 과정이 진행되고 Concurrent Mark 단계에서 방금 살아있다고 확인한 객체에서 참조하고 있는 객체들을 따라가면서 확인합니다.
이 단계의 특징은 다른 스레드가 실행 중인 상태에서 동시에 진행됩니다.
그 다음 Remark 단계에서는 ConcurrentMark 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인합니다.
마지막으로 Concurrent Sweep 단계에서는 쓰레기를 정리하는 작업을 실행합니다.
이 작업도 다른 스레드가 실행되고 있는 상황에서 진행됩니다.
이러한 단계로 진행되는 GC방식이기 때문에 stop-the-world 시간이 매우 짧습니다.
모든 애플리케이션의 응답속도가 중요할 때, CMS GC를 사용하며, Low Latency GC라고도 부릅니다.
하지만 이런 장점이외에 단점으로 다른 GC방식보다 메모리와 CPU를 더 많이 사용하는 점과 Compaction 단계가 기본적으로 제공되지 않는 점이 있습니다.
따라서, CMS GC를 사용할 때에는 신중히 검토한 후에 사용해야합니다.
그리고 조각난 메모리가 많아 Compaction 작업을 실행하면 다른 GC 방식의 stop-the-world 시간보다 stop-the-world 시간이 더 길기 때문에 Compaction 작업이 얼만 나 자주 오랫동안 수행되는지 확인해야 합니다.
G1 GC는 지금까지 설명했던 Young영역과 Old영역과 전혀 다른 방식으로 동작합니다.
G1 GC는 바둑판의 영역으로 약 2000개의 구역을 사용합니다. 각 영역에 객체를 할당하고 GC를 실행합니다.
그러다가, 해당 영역이 꽉 차면 다른 영역에서 객체를 할당하고 GC를 실행합니다.
즉, Young의 세가지 영역에서 Old영역으로 이동하는 단계가 사라진 GC방식으로 이해할 수 있습니다.
G1 GC는 장기적으로 CMS GC를 대체하기 위해 만들어졌습니다.