🤔가비지 컬렉션(Garbage Collection, GC)이란?
가비지 컬렉션은 자바의 메모리 관리 방법 중 하나로, JVM의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된(사용하지 않는) 메모리 객체를 모아 주기적으로 제거하는 프로세스를 뜻한다.
C/C++은 이러한 가비지 컬렉션이 없어 프로그래머가 수동으로 메모리 할당과 해제를 일일이 해줘야 한다. Java는 개발자 입장에서 메모리 관리와 메모리 누수 문제에 대해 신경쓰지 않아도 되어 좀더 수월하게 개발 할 수 있다.
하지만, 이런 가비지 컬렉션에도 단점이 있는데, 이런 단점을 Stop-The-World라고 한다.
자동으로 처리해준다 해도 메모리가 언제 해제되는지 정확하게 알수 없어 제어하기 힘들며, 가비지 컬렉션이 동작하는 동안에는 다른 동작을 멈추기 때문에 오버헤드가 발생되는 문제점이 있다.
이로 인해 GC가 너무 자주 실행되면 소프트웨어 성능 하락의 문제가 되기도 한다.
✅가비지 컬렉션의 대상
일반적으로 아래와 같은 경우에 GC의 대상이 된다.
1. 객체가 NULL인 경우 (String str = null)
2. 블럭 실행 종료 후, 블럭 안에서 생성된 객체
3. 부모 객체가 NULL인 경우, 포함하는 자식 객체
GC는 Weak Generational Hypothesis 에 기반하는데, 이게 무엇인지 알아보기 전에 GC의 메모리 해제 과정에 대해 살펴보자.
✅GC의 메모리 해제 과정
GC가 메모리를 해제할 때는 Mark And Sweep이란 알고리즘을 사용한다.

가비지 컬렉션은 대상 객체를 식별(Mark)하고, 제거(Sweep)하며 객체가 제거되어 파편화된 메모리 영역을 앞에서부터 채워나가는 작업(Compaction)을 수행하게 된다.
1️⃣ Mark
- Root Space로부터 그래프 순회를 통해 연결된 객체들을 찾아내어 어떤 객체를 참조하고 있는지 찾아서 마킹한다.
- 참조하고 있는 객체가 없는 객체가 바로 GC의 대상인 사용하지 않는 객체이다.
2️⃣Sweep
- Mark 과정에서 찾은 참조하고 있지 않은 객체들 즉, 사용하지 않는 객체를 Heap에서 제거한다.
3️⃣Compact
- Sweep 후에 남아있는 객체(사용중인 객체들)을 Heap의 시작 주소로 모아 묶는다.
- 이들을 묶음으로써 공간이 생기므로 새로운 메모리 할당 시에 더 쉽고 빠르게 진행이 가능하다.

✅Weak Generational Hypothesis
JVM의 Heap영역은 처음 설계될 때 다음과 같은 2가지 전제로 설계되었다.
- 대부분의 객체는 금방 접근 불가능한 상태(Unreachable)가 된다.
- 오래된 객체에서 새로운 객체로의 참조는 매우 적게 존재한다.
즉, 객체는 대부분 일회성이며, 메모리에 오랫동안 남아있는 경우는 드물다는 것이다.
이 가설에 기반하여 Java는 객체의 생존기간에 따라 물리적인 Heap영역을 Young영역과 Old 영역으로 나누게 되었다. 신규로 생성되는 객체는 Young 영역에, 오랫동안 살아남은 객체는 Old 영역에 보관한다.
💡Generational Garbage Collection
✅Generational Garbage Collection의 구조

Young 영역
- 새롭게 생성되는 객체가 할당되는 영역
- 대부분의 객체가 금방 Unreachable상태가 되기 때문에 많은 객체가 Young 영역에 생성되었다가 사라진다.
- Young 영역에 대한 가비지 컬렉션을 Minor GC라고 부른다.
Old 영역
- Young 영역에서 살아남은 객체가 복사되는 영역
- Young 영역보다 크게 할당되며, 영역의 크기가 큰 만큼 가비지는 적게 발생한다.
- Old 영역에 대한 가비지 컬렉션을 Major GC 또는 Full GC라고 부른다.
또 다시 힙 영역은 더욱 효율적인 GC를 위해 Young 영역을 3가지 영역으로 나눈다.

Eden
- new를 통해 새로 생성된 객체가 위치
- 장기적인 쓰레기 수집 후 살아남은 객체들은 Survivor영역으로 보냄
Survivor 0 / Survivor 1
- 최소 1번의 GC 이상 살아남은 객체가 존재하는 영역
- Survivor 0 또는 Survivor 1 둘중 하나는 꼭 비어 있어야 한다는 규칙이 존재.
✅Minor GC 과정

- Yong Generaion 영역은 짧게 살아남는 메모리들이 존재하는 공간이다.
- 모든 객체는 처음에 Young Generation에 생성된다.
- Old Generation에 비해 공간이 상대적으로 작기 때문에 메모리 상의 객체를 찾아 제거하는데 적은 시간이 걸린다.
1. 처음 생성된 객체는 Young Generation 영역의 일부인 Eden 영역에 위치

2. 객체가 계속 생성되어 Eden 영역이 꽉차게 되면 Minor GC가 발생

3. Mark 동작을 통해 참조되는 객체 탐색

4. Eden 영역에서 살아남은 객체는 Survivor 영역으로 이동

5. Eden영역에서 사용되지 않은 객체의 메모리를 해제(sweep)

6. 살아남은 모든 객체들의 age 값이 1씩 증가

7. 또다시 Eden 영역에 신규 객체들로 가득차게 되면 다시 Minor GC가 발생되고 mark 실행

8. marking한 객체들을 비어있는 Survivor 1(다른 Survivor 영역)으로 이동하고 sweep

9. 다시 살아남은 모든 객체들의 age 1씩 증가

10. 이 모든 과정을 반복
✅Major GC 과정

- Old Generation은 길게 살아남는 메모리들이 존재하는 공간
- Old Generation의 객체들은 거슬러 올라가면 처음에는 Young Generation에 의해 시작되었으나, GC 과정 중에 제거되지 않은 경우 age 임계값이 차게되어 이동된 객체들이다.
- Major GC는 객체들이 계속 Promotion되어 Old 영역의 메모리가 부족해지면 발생하게 된다.
1. 객체의 age가 임계값(예시는 8)에 도달하게 되면


3. 위 과정이 반복되어 Old Generaion 영역이 가득 차게되면 Major GC가 발생한다.

Major GC는 Old Generation 영역이 가득 찼을 때 실행된다. 이때 GC는 Old Generation에 존재하는 객체들을 검사해 참조되지 않는 객체들을 한꺼번에 정리한다.
Old Generation은 Young Generation보다 공간이 훨씬 크기 때문에, Major GC가 수행될 때는 더 많은 객체를 스캔하고 해제해야 한다. 그 결과 GC 처리 시간이 길어질 수 있다.
이때, Stop-The-World 문제가 발생하게 된다.
Major GC가 실행되는 동안에는 애플리케이션 스레드가 일시 중지되며, GC 작업이 완료될 때까지 모든 요청 처리가 멈추게 되는데, 이로 인해 지연이나 성능 저하가 발생할 수 있으며, 이를 흔히 GC 오버헤드 문제라고 부른다.
따라서 이런 문제를 해결하고 최적화하기 위해 다양한 가비지 컬렉션 알고리즘이 개발되었다.
가비지 컬렉션 알고리즘 종류
Serial GC

- 서버의 CPU 코어가 1개 일때 사용하기 위해 개발된 가장 단순한 GC
- GC를 처리하는 스레드가 1개여서 가장 Stop-The-World의 시간이 가장 길다
- 보통 실무에서는 사용하지 않는다.
Parallel GC
- Java 8의 디폴트 GC
- Serial GC와 기본적인 알고리즘은 같지만, Young 영역의 Minor GC를 멀티쓰레드로 수행(Old 영역은 여전히 싱글 스레드)

Parallel Old GC
- Parallel GC를 개선한 버전
- Young 영역 뿐만 아니라 Old 영역에서도 멀티스레드로 GC 수행
- 새로운 가비지 컬렉션 청소 방식인 Mark-Summary-Compact 방식을 이용
CMS GC
- 어플리케이션의 스레드와 GC 스레드가 동시에 실행되어 Stop-The-World 시간을 최대한 줄이기 위해 고안된 GC
- GC 과정이 매우 복잡하며 메모리 파편화 문제 존재
- GC 대상을 파악하는 과정이 복잡한 여러단계로 수행되기 때문에 다른 GC 대비 CPU 사용량이 높음
- Java14에서부터 사용이 중지
G1 GC
- CMS GC를 대체하기 위해 나온 GC
- Java 9+ 버전의 디폴트 GC로 지정
- 기존의 GC 알고리즘에서는 Heap 영역을 물리적으로 고정된 Young / Old 영역으로 나누어 사용하였지만, G1 gc는 아예 이러한 개념을 뒤엎는 Region이라는 개념을 새로 도입하여 사용.전체 Heap 영역을 Region이라는 영역으로 체스같이 분할하여 상황에 따라 Eden, Survivor, Old 등 역할을 고정이 아닌 동적으로 부여

Shenandoah GC
- Java 12에 release
- 레드햇에서 개발한 GC
- 기존 CMS가 가진 단편화, G1이 가진 pause의 이슈를 해결
- 강력한 Concurrency와 가벼운 GC 로직으로 heap 사이즈에 영향을 받지 않고 일정한 pause 시간이 소요가 특징

ZGC
- Java 15에 release
- G1의 Region 처럼, ZGC는 ZPage라는 영역을 사용하며, G1의 Region은 크기가 고정인데 비해, ZPage는 2mb 배수로 동적으로 운영됨. (큰 객체가 들어오면 2^ 로 영역을 구성해서 처리)
- ZGC가 내세우는 최대 장점 중 하나는 힙 크기가 증가하더도 'stop-the-world'의 시간이 절대 10ms를 넘지 않는다는 것
