이 글은 공부를 하면서 알게 된 내용들을 기록하는 글 입니다. 오류나 고쳐야 할 사항들이 있다면 지적 부탁드립니다!
✅ Garbage Collection (GC)란?
`JVM(Java Virtual Machine)`에서 제공하는 메모리 관리 기법으로서, 프로그램에서 더 이상 사용되지 않는 메모리를 자동으로 회수합니다.
`JVM`의 Heap 영역에 동적으로 할당했던 메모리 객체(Garbage)를 파악한 후, 이를 주기적으로 메모리 할당 해제합니다.
C나 C++이 개발자가 직접 메모리를 할당하고 할당 해제하는 언어와 달리, GC가 있는 언어의 경우 개발자가 메모리 관리를 하지 않아도 됩니다.
그렇기 때문에 개발자는 메모리 관리와 같은 일에 신경쓰지 않고 개발에만 신경쓰면 되며,
`Memory leak(메모리 누수)` 혹은 `Dangling Pointer(이미 해제된 메모리를 가리키는 포인터)`와 같은 문제를 예방할 수 있습니다.
하지만 GC에도 단점이 존재하는데,
1️⃣ 메모리가 언제 해제되는지 정확하게 알 수 없기 때문에 제어하기 힘들고
2️⃣ GC가 동작하는 동안에는 GC를 실행하는 Thread를 제외한 모든 Thread의 동작이 멈추기 때문에 GC로 인한 Overhead가 발생할 수 있습니다.
2️⃣번을 다른 말로 `Stop-The-World` 라고 하는데, GC가 너무 자주 실행되면 `STW`로 인해 소프트웨어 성능 하락이 일어나기도 합니다.
✅ Garbage Collection의 대상인지 확인하는 방법
🔥 도달성(Reachability) 검사
GC는 `Heap 영역`에 있는 다양한 Object(객체)들 중, 어떤 객체들을 Garbage로 판단하는 것일까요?
GC는 `도달성(Reachability) 검사`를 통해, 해당 객체가 Garbage인지 여부를 판단합니다.

객체가 참조되고 있다면 Reachable로 구분되어 GC의 대상이 되지 않고,
객체가 참조되고 있지 않다면 Unreachable로 구분되어 GC의 대상이 됩니다.
✅ GC의 동작 과정
🔥 GC의 기본 전제
1️⃣ 대부분의 객체는 생성 후 금방 죽는다 (Young)
2️⃣ 오래 살아남은 객체는 계속 살아남을 것이다 (Old)
🔥 메모리 구조

JVM 메모리의 Heap 영역은 다음과 같은 구조를 가지고 있습니다.
우선 크게 Young Generation, Old Generation 이렇게 두 개의 영역으로 나눌 수 있습니다.
1️⃣ Young Generation
Young Generation은 new()와 같은 연산자를 통해 생성된 객체가 할당(Allocation) 되는 영역입니다.
Young Generation에서 이루어지는 가비지 컬렉션을 `Minor GC`라고 부릅니다.
- Eden
새로 생성된 객체가 할당되는 영역 - Survivor 1/0
Minor GC에서 살아남은 객체가 이동하는 곳
Survivor 0 혹은 Suvivor 1, 두 공간 중 하나는 꼭 비어있어야 하며, 0과 1의 영역을 왔다갔다하며 사용합니다.
2️⃣ Old Generation
Old Generation은 Young Generation에서 오랫동안 Reachable 상태를 유지한 객체가 넘어오는 곳입니다.
Young 영역보다 더 큰 공간을 가지고 있으며, 이 곳에서 이루어지는 가비지 컬렉션을 `Major GC` 혹은 `Full GC`라고 부릅니다.
🔥 Minor GC의 동작 과정
1️⃣ Minor GC (Young Generation)
① 새로 생성되는 객체는 모두 Young Generation의 `Eden` 영역에 생성
② 그러던 중, Eden 영역이 꽉 차게 되면 이 때 `Minor GC`가 실행
③ 이 때, 도달성(Reachability) 검사를 통해 Reachable한 객체의 경우 `Survivor` 영역으로 이동
④ 이 과정에서 각 객체의 `age`가 1씩 증가합니다.
age는 객체가 survivor 영역에서 살아남은 횟수를 의미하며,
age가 임계값(age threshold)에 다다르면 Old Generation으로 이동하는 Promotion(승격)을 고려하게 됩니다.
🧐 Reachable한 객체들을 survivor 영역으로 이동하던 중에, survivor의 영역이 꽉차면 어떻게 되나요?
→ age threshold에 다다르지 않은 객체들을 Old 영역으로 옮기는 조기 승격(Premature Promotion)을 고려하게 됩니다.
🧐 그럼 모든 객체들이 Old 영역으로 조기 승격하게 되나요?
→ 아닙니다. 이 경우 객체의 크기를 기준으로 승격 여부를 확인하기도 합니다.
① 큰 객체부터 승격을 진행 ② 작은 객체는 Survivor 영역에 유지 하는 방향으로 진행합니다.
이 외에도 age threshold를 동적으로 설정하는 Dynamic Age Threshold, Eden와 Survivor의 비율을 동적으로 조절하는 Adapting size와 같은 방법이 있으며, 이는 GC의 종류마다 처리하는 방법이 달라집니다.
🔥 Major GC의 동작 과정
① Survivor 영역에서 age threshold에 도달한 객체는 Old 영역으로 이동
② 그러던 중, Old 영역이 꽉 차게 되면 `Major GC` 동작
③ 도달성(Reachability) 검사를 통해 Unreachable한 객체를 대상으로 선정
④ GC의 대상이 되는 객체들을 한 번에 삭제
Old Generation(영역)이 꽉 찼을 때 발생하며, Old 영역의 크기가 크기 때문에 소요 시간이 걸린다는 단점이 있습니다.
Old 영역의 모든 객체에 대해 도달성(Reachability) 조사 후, Unreachable한 Obejct들을 한 번에 삭제하는 방식으로 동작합니다.
🔥 Stop-The-World
GC가 동작하는 동안에는 GC를 실행하는 Thread를 제외한 모든 Thread의 동작이 멈추고, GC의 실행 이후에 중단되었던 작업들을 다시 시작합니다.
이를 Stop-The-World, 짧게는 STW라고 부릅니다.
Minor GC는 비교적 공간이 작은 Young Generation에서 발생하기 때문에 STW(Stop-The-World)가 비교적 짧은 방면,
Major GC의 경우 공간이 크기 때문에 STW가 비교적 길다는 특징을 가지고 있습니다.
✅ GC의 종류
JVM에서 사용하는 GC 구현체를 크게 네 가지로 나눠볼 수 있습니다.
🔥 Serial GC

단일 스레드를 사용해 GC 작업을 하는 방식으로 가장 단순한 방법이지만, `STW(Stop-The-World)` 시간이 길다는 단점이 있습니다.
싱글 코어 환경이나 메모리가 적은 환경(임베디드 환경) 혹은 간단한 어플리케이션의 경우 Serial GC가 적합할 수 있습니다.
🔥 Parallel GC

Serial GC의 더 발전된 방법으로서, Java 8의 디폴트 GC입니다.
Serial 방식과 거의 유사하나, GC 작업(Minor, Major 모두)을 멀티 스레드로 수행합니다.
GC 중에는 동일하게 `Stop-The-World` 상태가 발생하지만, 작업을 병렬 처리하여 전체 GC 시간을 단축하는데에 초점을 맞추고 있습니다.
🔥 G1 GC

G1(Garbage First) GC는 기존의 방식과 다르게, Heap 메모리를 `크기가 같은 여러 Region`으로 나누고, 해당 Region을 Young/Old(Tenured) 영역으로 동적 할당하는 방식입니다.
- Eden Region
새로 생성된 객체들이 할당되는 영역으로 Young Generation의 일부 - Survivor Region
Eden Region에서 Minor GC에서 살아남은 객체들이 이동하는 영역으로 Young Generation의 일부 - Tenured/Old Region
Survivor Region에서 age threshold를 넘은 객체들이 저장되는 영역으로 Old Generation을 의미 - Humongous Region
Region 크기의 50%를 초과하는 큰 객체들을 저장하는 영역 - Available Region
아직 사용되지 않은 빈 영역을 의미하며, 필요에 따라 특정 영역으로 할당될 수 있음
G1 GC는 Java 9 이후 버전에서 디폴트 GC로 설정되어 있습니다.
G1 GC는 어플리케이션이 실행되는 중에 살아있는 객체를 계속 Mark하는데, 이 과정을 통해 Garbage 비율을 기록합니다.
G1 GC는 Heap 전체를 검사하는 대신,
필요에 따라 특정 Region(Garbage 비율이 높은 Region)을 먼저 수집(Collect)하는 방식으로 수행합니다.
🔥 Z GC

`Z GC`는 G1 GC와 같이 Region을 활용하는 방식이지만, Region의 크기를 동적으로 활용한다는 차이점을 가지고 있습니다.
`Z GC`는 10ms 이내의 매우 낮은 중단 시간 - STW(Stop The World)를 목표로 합니다.
대규모 힙 메모리를 사용하며, 짧은 지연 시간이 필수인 경우 `Z GC` 사용을 권장합니다.
Java 11에서부터 실험적으로 도입되었으며, 15 버전부터 정식 기능을 지원하고 있습니다. 현재 `G1 GC`를 기본 GC로 제공하고 있기 때문에 `Z GC`를 옵션으로 설정하여 사용할 수 있습니다.
✅ 참고 자료 & 링크
- JVM Garbage Collectors | Baeldung
- Claude
- Chat GPT
이 글은 공부를 하면서 알게 된 내용들을 기록하는 글 입니다. 오류나 고쳐야 할 사항들이 있다면 지적 부탁드립니다!
✅ Garbage Collection (GC)란?
JVM(Java Virtual Machine)
에서 제공하는 메모리 관리 기법으로서, 프로그램에서 더 이상 사용되지 않는 메모리를 자동으로 회수합니다.
JVM
의 Heap 영역에 동적으로 할당했던 메모리 객체(Garbage)를 파악한 후, 이를 주기적으로 메모리 할당 해제합니다.
C나 C++이 개발자가 직접 메모리를 할당하고 할당 해제하는 언어와 달리, GC가 있는 언어의 경우 개발자가 메모리 관리를 하지 않아도 됩니다.
그렇기 때문에 개발자는 메모리 관리와 같은 일에 신경쓰지 않고 개발에만 신경쓰면 되며,
Memory leak(메모리 누수)
혹은 Dangling Pointer(이미 해제된 메모리를 가리키는 포인터)
와 같은 문제를 예방할 수 있습니다.
하지만 GC에도 단점이 존재하는데,
1️⃣ 메모리가 언제 해제되는지 정확하게 알 수 없기 때문에 제어하기 힘들고
2️⃣ GC가 동작하는 동안에는 GC를 실행하는 Thread를 제외한 모든 Thread의 동작이 멈추기 때문에 GC로 인한 Overhead가 발생할 수 있습니다.
2️⃣번을 다른 말로 Stop-The-World
라고 하는데, GC가 너무 자주 실행되면 STW
로 인해 소프트웨어 성능 하락이 일어나기도 합니다.
✅ Garbage Collection의 대상인지 확인하는 방법
🔥 도달성(Reachability) 검사
GC는 Heap 영역
에 있는 다양한 Object(객체)들 중, 어떤 객체들을 Garbage로 판단하는 것일까요?
GC는 도달성(Reachability) 검사
를 통해, 해당 객체가 Garbage인지 여부를 판단합니다.

객체가 참조되고 있다면 Reachable로 구분되어 GC의 대상이 되지 않고,
객체가 참조되고 있지 않다면 Unreachable로 구분되어 GC의 대상이 됩니다.
✅ GC의 동작 과정
🔥 GC의 기본 전제
1️⃣ 대부분의 객체는 생성 후 금방 죽는다 (Young)
2️⃣ 오래 살아남은 객체는 계속 살아남을 것이다 (Old)
🔥 메모리 구조

JVM 메모리의 Heap 영역은 다음과 같은 구조를 가지고 있습니다.
우선 크게 Young Generation, Old Generation 이렇게 두 개의 영역으로 나눌 수 있습니다.
1️⃣ Young Generation
Young Generation은 new()와 같은 연산자를 통해 생성된 객체가 할당(Allocation) 되는 영역입니다.
Young Generation에서 이루어지는 가비지 컬렉션을 Minor GC
라고 부릅니다.
- Eden
새로 생성된 객체가 할당되는 영역 - Survivor 1/0
Minor GC에서 살아남은 객체가 이동하는 곳
Survivor 0 혹은 Suvivor 1, 두 공간 중 하나는 꼭 비어있어야 하며, 0과 1의 영역을 왔다갔다하며 사용합니다.
2️⃣ Old Generation
Old Generation은 Young Generation에서 오랫동안 Reachable 상태를 유지한 객체가 넘어오는 곳입니다.
Young 영역보다 더 큰 공간을 가지고 있으며, 이 곳에서 이루어지는 가비지 컬렉션을 Major GC
혹은 Full GC
라고 부릅니다.
🔥 Minor GC의 동작 과정
1️⃣ Minor GC (Young Generation)
① 새로 생성되는 객체는 모두 Young Generation의 Eden
영역에 생성
② 그러던 중, Eden 영역이 꽉 차게 되면 이 때 Minor GC
가 실행
③ 이 때, 도달성(Reachability) 검사를 통해 Reachable한 객체의 경우 Survivor
영역으로 이동
④ 이 과정에서 각 객체의 age
가 1씩 증가합니다.
age는 객체가 survivor 영역에서 살아남은 횟수를 의미하며,
age가 임계값(age threshold)에 다다르면 Old Generation으로 이동하는 Promotion(승격)을 고려하게 됩니다.
🧐 Reachable한 객체들을 survivor 영역으로 이동하던 중에, survivor의 영역이 꽉차면 어떻게 되나요?
→ age threshold에 다다르지 않은 객체들을 Old 영역으로 옮기는 조기 승격(Premature Promotion)을 고려하게 됩니다.
🧐 그럼 모든 객체들이 Old 영역으로 조기 승격하게 되나요?
→ 아닙니다. 이 경우 객체의 크기를 기준으로 승격 여부를 확인하기도 합니다.
① 큰 객체부터 승격을 진행 ② 작은 객체는 Survivor 영역에 유지 하는 방향으로 진행합니다.
이 외에도 age threshold를 동적으로 설정하는 Dynamic Age Threshold, Eden와 Survivor의 비율을 동적으로 조절하는 Adapting size와 같은 방법이 있으며, 이는 GC의 종류마다 처리하는 방법이 달라집니다.
🔥 Major GC의 동작 과정
① Survivor 영역에서 age threshold에 도달한 객체는 Old 영역으로 이동
② 그러던 중, Old 영역이 꽉 차게 되면 Major GC
동작
③ 도달성(Reachability) 검사를 통해 Unreachable한 객체를 대상으로 선정
④ GC의 대상이 되는 객체들을 한 번에 삭제
Old Generation(영역)이 꽉 찼을 때 발생하며, Old 영역의 크기가 크기 때문에 소요 시간이 걸린다는 단점이 있습니다.
Old 영역의 모든 객체에 대해 도달성(Reachability) 조사 후, Unreachable한 Obejct들을 한 번에 삭제하는 방식으로 동작합니다.
🔥 Stop-The-World
GC가 동작하는 동안에는 GC를 실행하는 Thread를 제외한 모든 Thread의 동작이 멈추고, GC의 실행 이후에 중단되었던 작업들을 다시 시작합니다.
이를 Stop-The-World, 짧게는 STW라고 부릅니다.
Minor GC는 비교적 공간이 작은 Young Generation에서 발생하기 때문에 STW(Stop-The-World)가 비교적 짧은 방면,
Major GC의 경우 공간이 크기 때문에 STW가 비교적 길다는 특징을 가지고 있습니다.
✅ GC의 종류
JVM에서 사용하는 GC 구현체를 크게 네 가지로 나눠볼 수 있습니다.
🔥 Serial GC

단일 스레드를 사용해 GC 작업을 하는 방식으로 가장 단순한 방법이지만, STW(Stop-The-World)
시간이 길다는 단점이 있습니다.
싱글 코어 환경이나 메모리가 적은 환경(임베디드 환경) 혹은 간단한 어플리케이션의 경우 Serial GC가 적합할 수 있습니다.
🔥 Parallel GC

Serial GC의 더 발전된 방법으로서, Java 8의 디폴트 GC입니다.
Serial 방식과 거의 유사하나, GC 작업(Minor, Major 모두)을 멀티 스레드로 수행합니다.
GC 중에는 동일하게 Stop-The-World
상태가 발생하지만, 작업을 병렬 처리하여 전체 GC 시간을 단축하는데에 초점을 맞추고 있습니다.
🔥 G1 GC

G1(Garbage First) GC는 기존의 방식과 다르게, Heap 메모리를 크기가 같은 여러 Region
으로 나누고, 해당 Region을 Young/Old(Tenured) 영역으로 동적 할당하는 방식입니다.
- Eden Region
새로 생성된 객체들이 할당되는 영역으로 Young Generation의 일부 - Survivor Region
Eden Region에서 Minor GC에서 살아남은 객체들이 이동하는 영역으로 Young Generation의 일부 - Tenured/Old Region
Survivor Region에서 age threshold를 넘은 객체들이 저장되는 영역으로 Old Generation을 의미 - Humongous Region
Region 크기의 50%를 초과하는 큰 객체들을 저장하는 영역 - Available Region
아직 사용되지 않은 빈 영역을 의미하며, 필요에 따라 특정 영역으로 할당될 수 있음
G1 GC는 Java 9 이후 버전에서 디폴트 GC로 설정되어 있습니다.
G1 GC는 어플리케이션이 실행되는 중에 살아있는 객체를 계속 Mark하는데, 이 과정을 통해 Garbage 비율을 기록합니다.
G1 GC는 Heap 전체를 검사하는 대신,
필요에 따라 특정 Region(Garbage 비율이 높은 Region)을 먼저 수집(Collect)하는 방식으로 수행합니다.
🔥 Z GC

Z GC
는 G1 GC와 같이 Region을 활용하는 방식이지만, Region의 크기를 동적으로 활용한다는 차이점을 가지고 있습니다.
Z GC
는 10ms 이내의 매우 낮은 중단 시간 - STW(Stop The World)를 목표로 합니다.
대규모 힙 메모리를 사용하며, 짧은 지연 시간이 필수인 경우 Z GC
사용을 권장합니다.
Java 11에서부터 실험적으로 도입되었으며, 15 버전부터 정식 기능을 지원하고 있습니다. 현재 G1 GC
를 기본 GC로 제공하고 있기 때문에 Z GC
를 옵션으로 설정하여 사용할 수 있습니다.
✅ 참고 자료 & 링크
- JVM Garbage Collectors | Baeldung
- Claude
- Chat GPT