Garbage Collection(이하 GC)은 자바의 메모리 관리 방법 중의 하나로 Java Virtual Machine(이하 JVM)의 heap 영역에서 동적으로 할당했던 메모리 중 더 이상 필요 없게 된 메모리 객체를 모아 주기적으로 제거하는 프로세스입니다.
- weak generational hypothesis
- 대부분의 객체는 이른 시점 내에서 접근 불가능 상태(unreachable)가 된다.
- 오래된 객체(Old)에서 젋은 객체(Young)로의 참조는 아주 적게 존재한다.
-
장점
- GC가 프로그래머 대신 메모리 관리를 해줍니다.
- 메모리 누수(memory leak) 등의 문제로부터 자유로워질 수 있습니다.
- 따라서 개발에 더욱 집중할 수 있습니다.
-
단점
- 메모리가 해제되는 시점을 정확히 알 수 없습니다.
- GC가 동작하는 동안 모든 것이 멈춥니다. 이를 stop-the-world라고 합니다.
stop-the-world
GC를 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것입니다. stop-the-world가 발생하면 GC를 실행하는 쓰레드를 제외한 모든 쓰레드는 작업을 멈추게됩니다.
GC는 자바에만 존재할까?
GC는 C#, JS, Python 등의 다양한 언어에서도 기본적으로 제공됩니다.
C언어와 C++같은 언어에서는 프로그래머가 직접 malloc(), free()
등의 함수를 통해 메모리 할당과 해제를 해줘야합니다.
Azul Systems의 CTO, co-founder인 Gil Tene님이 GC에 대해 설명해주는 영상에서는 GC가 가장 효율적인 메모리 관리 기법이라고 소개하고 있습니다.
Understanding Java Garbage Collection
GC 동작 과정
GC Root
힙 외부에서 접근할 수 있는 변수나 오브젝트를 의미한다.
- 실행중인 쓰레드 (Active Thread)
- Method Area의 정적 변수 (Static Variable)
- Stack의 로컬 변수 (Local Variable)
- Native Method Stack의 JNI 레퍼런드 (JNI Refernece)
Garbage Object
- Reachable: GC root로 부터 객체가 참조되고 있는 상태
- Unreachable: GC root로 부터 객체가 참조되고 있는 않은 상태
위 두 가지 상태 중 unreachable인 상태는 GC의 대상이 된다.
Mark and Sweep
GC 대상 객체를 식별(Mark)하고 제거(Sweep)하며 객체 제거로 인해 발생한 메모리 파편화를 앞에서부터 채워주는 작업(Compaction)을 수행한다.
- 이를 통해 root로부터 연결이 끊긴 순환 참조 객체들을 모두 지울 수 있다.
- GC 알고리즘 종류에 따라 compact 작업은 생략될 수 있다.

Young Generation
새로운 객체들이 할당되는 영역
대부분의 객체제가 Young 영역에서 생성 및 삭제된다.
-
3가지 영역의 구성
- Eden 영역
- survivor0, survivor1 영역
-
age 값
- Survivor 영역에서 객체의 객체가 살아남은 횟수를 의미하는 값이며, Object Header에 기록된다.
- 만일 age 값이 임계값에 다다르면 Promotion(Old 영역으로 이동) 여부를 결정한다.
- Survivor 영역의 제한 조건으로 Survivor 영역 중 반드시 1개는 사용되어야 하고, 나머지는 비어 있어야 한다.
- JVM 중 가장 일반적인 HotSpot JVM의 경우 이 age의 기본 임계값은 31이다. 객체 헤더에 age를 기록하는 부분이 6 bit로 되어 있기 때문이다.
- Minor GC
- 새로 생성한 대부분의 객체는 Eden 영역에 위치
- 객체가 계속 생성되어 Eden 영역이 꽉차게 되고 Minor GC가 실행
- Mark 동작을 통해 reachable 객체를 탐색
- Eden 영역에서 살아남은 객체는 1개의 Survivor 영역으로 이동
- Eden 영역에서 사용되지 않는 객체(unreachable)의 메모리를 해제(sweep)
- 살아남은 모든 객체들은 age값이 1씩 증가
- 또다시 Eden 영역에 신규 객체들로 가득 차게 되면 다시한번 minor GC 발생하고 mark
- marking 한 객체들을 비어있는 Survival 1으로 이동하고 sweep
- 다시 살아남은 모든 객체들은 age가 1씩 증가
- 이러한 과정을 반복
Old Generation
Young Generation에서 오랫동안 살아남은 객체들이 존재하는 영역
보다 큰 영역이 할당되며 GC가 자주 발생하지 않는다.
- Major GC, Full GC
- 객체의 age가 임계값(여기선 8로 설정)에 도달하게 되면,
- 이 객체들은 Old Generation 으로 이동된다. 이를 promotion 이라 부른다.
- 위의 과정이 반복되어 Old Generation 영역의 공간(메모리)가 부족하게 되면 Major GC가 발생되게 된다.
Old Generation의 객체가 Young Generation의 객체를 참조하는 경우
Old 영역에 있는 객체가 young 영역의 객체를 참조할 때마다 정보가 card table에 참조된다.
minor gc 실행 시에는 old 영역에 있는 모든 객체의 참조를 확인하지 않고, 이 card table을 통해 GC 대상인지 식별합니다.
카드 테이블은 minor GC를 빠르게 할 수 있도록 하는 장치인 wrtie barrier를 사용하여 관리한다.
GC 알고리즘 종류

GC는 앞서 설명한 두 가지의 치명적인 단점이 있기 때문에 다양한 알고리즘이 탄생하고 있습니다.
따라서 본인의 프로젝트에 최적화하기 위해 튜닝 과정이 필요할 수 있습니다.
Serial GC
- 서버의 CPU 코어가 1개일 때 사용하기 위해 개발된 가장 단순한 GC
- GC를 처리하는 쓰레드가 1개 (싱글 쓰레드) 이어서 가장 stop-the-world 시간이 길다
- Minor GC 에는 Mark-Sweep을 사용하고, Major GC에는 Mark-Sweep-Compact를 사용한다.
- 보통 실무에서 사용하는 경우는 없다 (디바이스 성능이 안좋아서 CPU 코어가 1개인 경우에만 사용)

Parallel GC
- Java 8의 디폴트 GC
- Serial GC와 기본적인 알고리즘은 같지만, Young 영역의 Minor GC를 멀티 쓰레드로 수행 (Old 영역은 여전히 싱글 쓰레드)
- Serial GC에 비해 stop-the-world 시간 감소

Parallel Old GC (Parallel Compacting Collector)
- Parallel GC를 개선한 버전
- Young 영역 뿐만 아니라, Old 영역에서도 멀티 쓰레드로 GC 수행
- 새로운 가비지 컬렉션 청소 방식인 Mark-Summary-Compact 방식을 이용 (Old 영역도 멀티 쓰레드로 처리)

CMS GC (Concurrent Mark Sweep)
- 어플리케이션의 쓰레드와 GC 쓰레드가 동시에 실행되어 stop-the-world 시간을 최대한 줄이기 위해 고안된 GC
- 단, GC 과정이 매우 복잡해짐.
- GC 대상을 파악하는 과정이 복잡한 여러단계로 수행되기 때문에 다른 GC 대비 CPU 사용량이 높다
메모리 파편화 문제
- CMS GC는 Java9 버젼부터 deprecated 되었고 결국 Java14에서는 사용이 중지

G1 GC (Garbage First)
- CMS GC를 대체하기 위해 jdk 7 버전에서 최초로 release된 GC
- Java 9+ 버전의 디폴트 GC로 지정
- 4GB 이상의 힙 메모리, Stop the World 시간이 0.5초 정도 필요한 상황에 사용 (Heap이 너무작을경우 미사용 권장)
- 기존의 GC 알고리즘에서는 Heap 영역을 물리적으로 고정된 Young / Old 영역으로 나누어 사용하였지만, G1 gc는 아예 이러한 개념을 뒤엎는 Region이라는 개념을 새로 도입하여 사용.전체 Heap 영역을 Region이라는 영역으로 체스같이 분할하여 상황에 따라 Eden, Survivor, Old 등 역할을 고정이 아닌 동적으로 부여
- Garbage로 가득찬 영역을 빠르게 회수하여 빈 공간을 확보하므로, 결국 GC 빈도가 줄어드는 효과를 얻게 되는 원리

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

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

출처