| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- Oracle Express Edition
- oracle 18c
- Oracle 18c 설치
- Oracle 초기 사용자
- ORA-12899
- 윈도우 Oracle
- Oracle 테이블 띄어쓰기
- Oracle 사용자명
- 무료 오라클 데이터베이스
- Oracle 18c HR
- 오라클 캐릭터셋 조회
- ORA-00922
- Orace 18c
- Oracle 테이블 대소문자
- 오라클 캐릭터셋 확인
- ora-01722
- 서평단
- Oracle 18c HR schema
- Oracle 윈도우 설치
- 비전공자를 위한 데이터베이스 입문
- 무료 오라클 설치
- oracle
- Oracle 사용자명 입력
- 오라클 캐릭터셋 변경
- Today
- Total
The Nirsa Way
[Java GC] Young Generation의 객체 할당과 수집: TLAB과 Survivor Space의 복사 수집 본문
[Java GC] Young Generation의 객체 할당과 수집: TLAB과 Survivor Space의 복사 수집
KoreaNirsa 2026. 7. 29. 11:36Young Generation의 객체 할당과 수집: TLAB과 Survivor Space의 복사 수집
새로운 객체는 Young Generation 안에서 실제로 어떻게 만들어질까요? 여러 스레드가 동시에 객체를 생성하면 같은 메모리 공간을 두고 충돌하지는 않을까요?
또한 Young GC가 발생했을 때 살아남은 객체는 어디로 이동하며, Survivor 영역을 두 개 사용하는 이유는 무엇일까요?
이 질문을 이해하려면 다음 두 가지 구조를 함께 봐야 합니다.
- TLAB : 여러 스레드가 객체를 빠르게 할당하기 위한 구조
- Survivor Space의 복사 수집 : Young GC에서 살아남은 객체를 다른 Survivor Space나 Old Generation으로 이동시키는 방식
먼저 정리하자면 아래와 같습니다.
- 대부분의 새 객체는 Young Generation의 Eden 영역에 할당됩니다.
- TLAB은 Eden의 일부를 스레드별로 나누어 객체를 빠르게 할당할 수 있게 합니다.
- Young GC가 발생하면 Eden과 현재 사용 중인 Survivor에서 살아남은 객체를 비어 있는 Survivor로 복사합니다.
- 두 Survivor 영역은 From과 To 역할을 번갈아 수행합니다.
- 여러 번 살아남거나 Survivor에 들어갈 수 없는 객체는 Old Generation으로 승격될 수 있습니다.
Young Generation은 어떻게 구성될까
전통적인 세대별 힙 구조에서 Young Generation은 다음과 같이 구성됩니다.

각 영역의 역할은 다음과 같습니다.
| 영역 | 역할 |
| Eden | 대부분의 새 객체가 처음 할당되는 영역입니다. |
| Survivor 0 | Young GC에서 살아남은 객체를 보관하는 영역입니다. |
| Survivor 1 | 다른 Survivor와 번갈아 생존 객체를 보관합니다. |
Survivor 0과 Survivor 1은 고정된 역할을 가지지 않습니다.
Young GC가 반복될 때 한쪽은 생존 객체를 읽어오는 From Survivor 다른 한쪽은 생존 객체를 복사해 넣는 To Survivor 역할을 맡습니다.
현재 사용 중인 Survivor
→ From Survivor
현재 비어 있는 Survivor
→ To Survivor
다음 Young GC에서는 두 영역의 역할이 서로 바뀝니다.
새 객체는 Eden에서 시작합니다
일반적인 세대별 힙 구조에서는 대부분의 새 객체가 Eden 영역에 할당됩니다.
User user = new User();
Order order = new Order();
String message = new String("hello");
개념적으로는 다음과 같은 흐름입니다.

애플리케이션이 실행되는 동안 객체 생성은 매우 자주 발생할 수 있습니다.
웹 애플리케이션에서는 요청 하나를 처리하는 동안에도 다음과 같은 객체가 만들어질 수 있습니다.
- 요청 DTO
- 응답 DTO
- 문자열
- 컬렉션
- 조회 결과 객체
- 임시 계산 객체
- 람다와 스트림 처리 과정에서 생성되는 객체
요청 수신
↓
DTO 생성
↓
서비스 객체 생성 및 가공
↓
응답 객체 생성
↓
요청 종료 후 다수 객체가 더 이상 필요하지 않음
약한 세대 가설에 따르면 이렇게 생성되는 많은 객체는 비교적 빠르게 사용이 끝납니다.
따라서 새 객체를 Eden에 모아 두면 Young GC를 통해 짧은 수명의 객체를 집중적으로 회수할 수 있습니다.
여러 스레드가 동시에 객체를 만들면 어떻게 될까
힙의 Eden 영역은 여러 애플리케이션 스레드가 함께 사용하는 공간입니다.
다음과 같이 여러 요청 처리 스레드가 동시에 객체를 생성할 수 있습니다.

객체를 연속된 메모리 공간에 배치한다면 JVM은 다음 객체가 들어갈 위치를 나타내는 정보를 관리해야 합니다.
이를 개념적으로 할당 포인터라고 볼 수 있습니다.
Eden 시작
[이미 사용한 공간][다음 할당 위치 →][남은 공간]
Eden 끝
객체를 할당할 때는 다음과 같이 처리할 수 있습니다.
현재 할당 포인터 확인
↓
객체 크기만큼 공간 확보
↓
객체 저장
↓
할당 포인터를 앞으로 이동
이처럼 포인터를 앞으로 이동하며 객체를 배치하는 방식을 범프 포인터 할당(Bump Pointer Allocation)이라고 합니다.
문제는 여러 스레드가 동일한 할당 포인터를 동시에 변경하려 할 수 있다는 점입니다.

모든 객체 할당마다 하나의 공유 포인터를 동기화해야 한다면 객체 생성 비용이 커질 수 있습니다.
이 문제를 줄이기 위해 사용하는 구조가 TLAB입니다.
TLAB이란
TLAB(Thread-Local Allocation Buffer)은 각 애플리케이션 스레드에 할당되는 작은 객체 할당 영역입니다.
핫스팟 JVM은 Eden의 일부를 스레드별 TLAB으로 나누어 사용할 수 있습니다.

각 스레드는 자신의 TLAB 안에서 독립적으로 객체를 할당합니다.
- 스레드 A : 스레드 A의 TLAB에서 객체 할당
- 스레드 B : 스레드 B의 TLAB에서 객체 할당
- 스레드 C : 스레드 C의 TLAB에서 객체 할당
다른 스레드의 TLAB을 직접 사용하지 않으므로, 일반적인 객체 할당 경로에서 공유 할당 포인터에 대한 경쟁을 줄일 수 있습니다.
즉 TLAB은 별도의 힙이 아니라, Eden 영역 일부를 특정 스레드가 객체 할당에 사용하도록 나눈 공간입니다.
TLAB에서는 객체를 어떻게 할당할까
각 TLAB은 자신만의 할당 시작 위치와 끝 위치를 가집니다.
스레드 A의 TLAB
[객체 A][객체 B][할당 포인터 →][남은 공간]
스레드가 새로운 객체를 만들면 다음 과정을 거칩니다.

TLAB 내부에서는 대체로 다음과 같은 단순한 연산으로 객체를 할당할 수 있습니다.
새 할당 위치
= 현재 할당 위치 + 객체 크기
객체를 놓을 공간이 TLAB 끝을 넘지 않는다면 현재 위치에 객체를 저장한 뒤 포인터만 앞으로 이동하면 됩니다.
할당 전
[객체 A][객체 B][포인터 →][빈 공간]
새 객체 C 할당
[객체 A][객체 B][객체 C][포인터 →][빈 공간]
이 방식은 일반적인 메모리 할당처럼 빈 공간 목록을 탐색하거나 복잡한 자료구조를 확인하지 않아도 되므로 빠르게 처리할 수 있습니다.
TLAB이 객체 할당을 빠르게 만드는 이유
TLAB을 사용하면 각 스레드는 대부분의 객체를 자신의 영역에서 할당할 수 있습니다.

| 구분 | 공유 영역에서 직접 할당 | TLAB에서 할당 |
| 할당 위치 | 여러 스레드가 공유합니다. | 스레드마다 별도로 가집니다. |
| 동시 접근 | 충돌 가능성이 있습니다. | 일반적인 할당에서는 충돌이 적습니다. |
| 포인터 관리 | 공유 포인터 갱신이 필요합니다. | 스레드별 포인터를 갱신합니다. |
| 객체 할당 비용 | 동기화 비용이 발생할 수 있습니다. | 범프 포인터 방식으로 빠르게 처리할 수 있습니다. |
TLAB이 있더라도 Eden 전체를 스레드별로 고정 분할해 영구적으로 소유하는 것은 아닙니다.
스레드의 객체 할당량에 따라 TLAB의 크기가 달라질 수 있고, 기존 TLAB을 다 사용하면 새로운 TLAB을 받을 수 있습니다.
모든 객체가 TLAB에 할당될까
TLAB은 일반적인 객체 할당의 주요 경로이지만, 모든 객체가 반드시 TLAB에 할당되는 것은 아닙니다.
다음과 같은 경우에는 TLAB 밖에서 할당될 수 있습니다.
- 현재 TLAB의 남은 공간보다 객체가 큰 경우
- 새로운 TLAB을 받는 것보다 외부 할당이 효율적인 경우
- JVM의 TLAB 설정이나 상태에 따라 외부 할당이 선택되는 경우
- 사용하는 가비지 컬렉터가 큰 객체를 별도로 처리하는 경우

따라서 다음처럼 이해하는 것이 정확합니다.
- 대부분의 일반적인 작은 객체 : TLAB에 빠르게 할당될 가능성 높음
- 모든 객체 : 반드시 TLAB에 할당되는 것은 아님
TLAB은 객체의 생존 기간을 관리하는 공간이 아닙니다.
TLAB에 생성된 객체도 실제로는 Eden에 존재하며, Young GC가 발생하면 다른 Eden 객체와 함께 생존 여부를 검사받습니다.
TLAB에 남은 공간은 어떻게 될까
스레드가 새로운 TLAB을 받으려 할 때 기존 TLAB에 작은 공간이 남아 있을 수 있습니다.
기존 TLAB
[객체 A][객체 B][사용하기 애매한 작은 공간]
남은 공간이 새 객체를 저장하기에 부족하다면 해당 공간이 즉시 다른 스레드의 일반 객체 할당에 사용되지는 않을 수 있습니다.
이처럼 TLAB별로 작게 남은 공간은 일시적인 내부 단편화처럼 보일 수 있습니다.
하지만 Young GC가 발생하면 Eden 영역이 정리되고, 다음 객체 할당을 위한 공간으로 다시 사용할 수 있습니다.
즉, TLAB은 할당 성능을 높이는 대신 일부 잔여 공간이 생길 수 있으며, JVM은 할당 패턴에 따라 TLAB 크기를 조절해 이러한 비용과 성능 사이의 균형을 맞춥니다.
Young GC는 언제 발생할까
객체가 계속 생성되면 Eden의 사용량이 증가합니다.
객체 생성
↓
Eden 내부의 TLAB 등에 할당
↓
Eden의 남은 공간 감소
↓
새 객체를 할당하기 어려워짐
↓
Young GC 필요
일반적으로 Eden에 새 객체를 할당할 공간이 부족해지면 Young Generation을 대상으로 수집이 발생할 수 있습니다.
이를 흔히 Young GC 또는 전통적으로 Minor GC라고 부릅니다.

Young GC는 단순히 Eden만 비우는 작업은 아닙니다.
일반적으로 Eden과 현재 사용중인 From Survivor에 있는 객체를 함께 처리합니다.
이 영역에서 살아 있는 객체를 찾아 비어 있는 To Survivor로 복사하거나 Old Generation으로 승격합니다.
Young GC가 시작되면 무엇을 확인할까
Young GC에서는 GC Root와 참조 관계를 바탕으로 Young Generation 객체의 생존 여부를 판단합니다.
다음과 같은 객체 관계가 있다고 가정하겠습니다.

GC Root에서 객체 A와 객체 B에는 도달할 수 있습니다.
객체 C, D, E에 다른 유효한 참조 경로가 없다면 해당 객체들은 가비지 객체가 될 수 있습니다.
객체 A
→ GC Root에서 직접 도달 가능
→ 생존
객체 B
→ 객체 A를 통해 도달 가능
→ 생존
객체 C, D, E
→ GC Root에서 도달 불가능
→ 회수 대상
이 글에서 설명하는 전통적인 Young Generation 모델에서는 살아남은 객체를 다른 영역으로 복사하거나 승격합니다.
가비지 객체를 하나씩 지우기보다 살아 있는 객체를 목적지 공간으로 이동시킨 뒤 기존 Eden과 From Survivor를 다시 사용하는 방식입니다.
Survivor 영역을 두 개 사용하는 이유
Young Generation에는 일반적으로 두 개의 Survivor 영역이 있습니다.
Survivor 0
Survivor 1
두 영역은 GC가 발생할 때 From과 To 역할을 번갈아 수행합니다.

Young GC가 발생하면 Eden과 From Survivor에서 살아남은 객체들을 To Survivor로 복사합니다.
Eden에서 살아남은 객체
+
From Survivor에서 다시 살아남은 객체
복사가 끝나면 기존 Eden과 From Survivor는 비워집니다.
# Young GC 전
Eden : 새 객체들이 존재
From Survivor : 이전 GC에서 살아남은 객체들이 존재
To Survivor : 비어 있음
# Young GC 후
Eden : 비워짐
기존 From Survivor : 비워짐
기존 To Survivor : 생존 객체를 가진 새로운 From Survivor가 됨
다음 Young GC에서는 두 Survivor의 역할이 반대로 바뀝니다.
Survivor Space의 복사 수집이란
Young GC에서는 살아 있는 객체를 현재 위치에 그대로 둔 채 가비지만 하나씩 지우는 대신, 생존 객체를 다른 공간으로 복사하거나 이동하는 방식을 사용할 수 있습니다.
전통적인 Eden과 두 Survivor Space 모델에서는 Eden과 현재 사용 중인 From Survivor의 생존 객체를 비어 있는 To Survivor로 복사합니다. 객체의 나이, Survivor의 여유 공간, 수집기의 정책에 따라 일부 객체는 Old Generation으로 승격될 수 있습니다.

이 글에서는 이 과정을 Survivor Space를 이용한 복사 수집이라고 표현합니다.
GC 알고리즘 이론에는 메모리를 From-space와 To-space로 나누는 세미스페이스 복사 수집기(Semispace Copying Collector)가 존재합니다. 그러나 HotSpot의 전통적인 Young Generation은 Eden과 두 Survivor Space로 구성되므로, Young Generation 전체를 단순한 세미스페이스 구조라고 부르는 것은 정확하지 않습니다.
# 고전적인 세미스페이스 복사 수집
From-space
↓ 생존 객체 복사
To-space
# 전통적인 HotSpot Young Generation 모델
Eden + From Survivor
↓ 생존 객체 복사 또는 승격
To Survivor 또는 Old Generation
핵심은 가비지 객체를 다른 위치로 옮기는 것이 아니라, 살아 있는 객체만 목적지 공간으로 이동시킨 뒤 기존 Eden과 From Survivor를 다시 사용할 수 있게 만드는 것입니다.
첫 번째 Young GC에서는 어떻게 이동할까
처음에는 Eden에 새로운 객체들이 생성됩니다.
Eden
[객체 A][객체 B][객체 C][객체 D]
Survivor 0
[비어 있음]
Survivor 1
[비어 있음]
객체 A와 객체 C만 살아 있다고 가정하겠습니다.
첫 번째 Young GC가 발생하면 살아남은 객체를 한쪽 Survivor로 복사합니다.

GC 이후의 상태는 다음과 같습니다.
Eden
[비어 있음]
Survivor 0
[객체 A][객체 C]
Survivor 1
[비어 있음]
이 시점에서 Survivor 0은 From Survivor 역할을 맡고, Survivor 1은 다음 GC에서 사용할 To Survivor가 됩니다.
두 번째 Young GC에서는 어떻게 이동할까
첫 번째 GC 이후 Eden에는 다시 새로운 객체가 생성됩니다.
Eden
[객체 E][객체 F][객체 G]
From Survivor
[객체 A][객체 C]
To Survivor
[비어 있음]
두 번째 Young GC에서는 Eden과 From Survivor에 있는 객체를 함께 확인합니다.
객체 A, E, G가 살아 있다고 가정해 보겠습니다.

GC 이후에는 기존 To Survivor가 새로운 From Survivor가 됩니다.
Eden
[비어 있음]
기존 From Survivor
[비어 있음]
기존 To Survivor
[객체 A][객체 E][객체 G]
→ 새로운 From Survivor
Survivor 영역의 역할은 계속 바뀝니다
Young GC가 반복될 때 두 Survivor 영역은 번갈아 사용됩니다.

이를 표로 정리하면 다음과 같습니다.
| Young GC | From Survivor | To Survivor |
| 첫 번째 | 없음 | Survivor 0 |
| 두 번째 | Survivor 0 | Survivor 1 |
| 세 번째 | Survivor 1 | Survivor 0 |
| 네 번째 | Survivor 0 | Survivor 1 |
첫 번째 Young GC에서는 기존 Survivor에 객체가 없을 수 있으므로 Eden의 생존 객체만 한쪽 Survivor로 복사됩니다.
이후부터는 Eden과 From Survivor의 생존 객체를 함께 To Survivor로 복사합니다.
왜 한 개의 Survivor만 사용하지 않을까
Survivor가 하나뿐이라면 현재 살아 있는 객체가 들어 있는 공간에 새로운 생존 객체를 다시 정리해서 배치해야 합니다.
즉 하나의 Survivor 안에서 기존 생존 객체 유지 + 새 생존 객체 추가 + 가비지 객체 제거 + 빈 공간 정리를 모두 수행하게 됩니다.
이러한 경우 같은 공간 안에서 살아 있는 객체와 제거할 객체를 구분하고, 객체를 재배치하는 과정이 복잡해질 수 있습니다.
하지만 두 Survivor를 사용하면 한쪽은 읽는 공간, 다른 한쪽은 쓰는 공간으로 명확하게 나눌 수 있습니다.

| 역할 | 설명 |
| From Survivor | 이전 GC에서 살아남은 객체가 저장된 공간입니다. |
| To Survivor | 이번 GC에서 살아남은 객체를 복사할 비어 있는 공간입니다. |
복사가 끝나면 기존 From Survivor 전체를 비울 수 있으므로, 살아 있는 객체들이 To Survivor에 연속적으로 모입니다.
# 복사 전
From Survivor
[생존 객체][가비지 객체][생존 객체][빈 공간]
복사 후
# To Survivor
[생존 객체][생존 객체][연속된 빈 공간]
이 방식은 생존 객체 복사와 메모리 압축을 함께 수행하는 효과를 가집니다.
Young GC에서 객체의 나이는 어떻게 바뀔까
Survivor로 이동한 객체는 Young GC에서 살아남은 횟수와 관련된 객체 나이(Age)를 가집니다.
Eden에서 새로 생성
→ Age 0
첫 번째 Young GC 생존
→ Age 1
두 번째 Young GC 생존
→ Age 2
세 번째 Young GC 생존
→ Age 3

객체 나이는 실제 시간이나 객체가 생성된 지 몇 초가 지났는지를 의미하지 않습니다.
Young GC에서 객체가 살아남은 횟수를 나타내는 개념에 가깝습니다.
여러 번의 GC에서 살아남은 객체는 단기 객체가 아닐 가능성이 높으므로 Old Generation으로 이동할 후보가 됩니다.
모든 생존 객체가 Survivor로 이동할까
Young GC에서 살아남은 객체가 항상 Survivor로 이동하는 것은 아닙니다.
생존 객체는 다음 두 경로 중 하나로 이동할 수 있습니다.

객체가 Old Generation으로 이동할 수 있는 대표적인 상황은 다음과 같습니다.
- 객체 나이가 승격 기준에 도달한 경우
- To Survivor에 생존 객체를 모두 저장할 공간이 부족한 경우
- JVM이 객체 나이 분포를 바탕으로 조기 승격을 결정한 경우
- 수집기나 객체 크기에 따라 별도 승격 경로가 적용되는 경우
즉 살아있는 객체를 Survivor에 보관할 수 있다면 To Survivor로 복사하고, Survivor의 공간이 부족하거나 승격 조건을 충족했다면 Old Generation으로 이동됩니다.
따라서 Young GC의 결과는 단순히 생존 또는 제거 두 가지로만 나뉘지 않습니다.
| 객체 상태 | Young GC 결과 |
| GC Root에서 도달할 수 없음 | 메모리가 회수됩니다. |
| 살아 있고 아직 승격 조건을 충족하지 않음 | To Survivor로 복사됩니다. |
| 살아 있고 승격 조건을 충족함 | Old Generation으로 승격됩니다. |
Young GC의 전체 처리 순서
Young GC의 전체 흐름을 정리하면 다음과 같습니다.

각 단계의 역할은 다음과 같습니다.
| 단계 | 처리 내용 |
| 객체 할당 | 대부분의 새 객체를 Eden 또는 TLAB에 할당합니다. |
| Young GC 발생 | Eden에 객체를 할당할 공간이 부족해지면 수집이 발생할 수 있습니다. |
| 생존 객체 탐색 | Eden과 From Survivor의 객체 중 도달 가능한 객체를 찾습니다. |
| 객체 복사 | 아직 젊은 생존 객체를 To Survivor로 복사합니다. |
| 객체 승격 | 승격 조건을 만족한 객체를 Old Generation으로 이동시킵니다. |
| 영역 초기화 | Eden과 기존 From Survivor를 비웁니다. |
| 역할 교체 | 기존 To Survivor가 새로운 From Survivor가 됩니다. |
TLAB과 Young GC는 어떻게 연결될까
TLAB은 객체를 생성하는 단계에서 사용되고, Young GC는 생성된 객체의 생존 여부를 판단하는 단계에서 사용됩니다.

두 구조의 역할은 명확하게 구분됩니다.
| 구조 | 사용 시점 | 목적 |
| TLAB | 객체 생성 시점 | 여러 스레드가 객체를 빠르게 할당하도록 돕습니다. |
| Eden | 객체 생성 이후 | 새 객체를 모아 두는 Young Generation의 영역입니다. |
| Survivor | Young GC 이후 | 살아남은 객체를 보관합니다. |
| Survivor 복사 수집 | Young GC 수행 중 | 생존 객체를 To Survivor나 Old로 이동시킵니다. |
| Old Generation | 장수 객체 관리 | 여러 번 살아남은 객체를 별도로 관리합니다. |
Young GC는 왜 효율적일 수 있을까
약한 세대 가설에 따르면 Young Generation에는 빠르게 사라지는 객체가 많이 존재할 가능성이 높습니다.
Young GC는 가비지 객체를 하나씩 이동시키지 않습니다.
살아 있는 객체만 다른 공간으로 복사하고, 기존 Eden과 From Survivor 전체를 비웁니다.

이 방식은 생존 객체의 수가 적으면 적은 수의 객체만 복사하고 기존 영역 전체를 빠르게 비우므로 생존 객체의 수가 적을수록 유리할 수 있습니다.
반대로 Young Generation에서 많은 객체가 계속 살아남는다면 복사해야 할 객체가 증가하고 Survivor와 Old Generation의 부담도 커질 수 있습니다.
Young GC가 발생하면 애플리케이션은 멈출까
Young GC의 중단 방식은 사용하는 가비지 컬렉터에 따라 다릅니다.
Serial GC, Parallel GC, G1 GC의 Young Collection처럼 생존 객체의 이동과 참조 갱신을 정지 구간에서 수행하는 수집기에서는 애플리케이션 스레드가 일시적으로 멈추는 STW(Stop-The-World)가 발생합니다.

다만 모든 세대별 수집기의 Young GC가 같은 방식으로 동작하는 것은 아닙니다. 세대별 ZGC나 세대별 Shenandoah처럼 주요 작업을 애플리케이션과 동시에 수행하도록 설계된 수집기도 있습니다.
따라서 다음처럼 이해하는 것이 정확합니다.
- 전통적인 HotSpot 수집기와 G1의 Young Collection : STW 구간에서 Young 영역을 수집
- 동시성을 강조한 세대별 수집기 : 주요 수집 작업의 상당 부분을 애플리케이션과 동시에 수행
STW 기반 Young GC의 실제 중단 시간은 다음 조건에 따라 달라질 수 있습니다.
- Young Generation 크기
- 객체 할당 속도
- 수집 시점의 생존 객체 수
- Survivor 공간 크기
- 승격되는 객체의 수
- 사용하는 가비지 컬렉터
- CPU와 GC 작업 스레드 수
Young GC라는 이름만으로 항상 짧다고 단정할 수는 없습니다. 특히 생존 객체나 승격 대상이 많으면 이동하고 갱신해야 하는 작업량도 증가할 수 있습니다.
Young GC를 이해할 때 주의할 점
Eden, 두 Survivor 영역, From과 To를 사용하는 구조는 세대별 가비지 컬렉션을 이해하기 위한 대표적인 모델입니다.
하지만 모든 가비지 컬렉터가 힙을 완전히 동일한 방식으로 구현하는 것은 아닙니다.
수집기에 따라 다음 요소가 달라질 수 있습니다.
- Young Generation의 물리적 배치
- Eden과 Survivor의 크기
- 객체가 이동하는 단위
- 큰 객체의 할당 위치
- 승격 기준
- Young GC의 병렬 처리 방식
- 리전 단위의 세대 관리 여부
예를 들어 리전 기반 수집기는 힙을 여러 개의 작은 리전으로 나누고, 각 리전에 Eden이나 Survivor 역할을 부여할 수 있습니다.
따라서 다음과 같이 이해하는 것이 좋습니다.
- Eden + Survivor 0 + Survivor 1 : 전통적인 Young Generation을 설명하는 대표적인 구조
- TLAB : 핫스팟에서 일반 객체 할당을 빠르게 만드는 주요 구조
- From / To Survivor : 생존 객체를 복사하는 기본 원리
- 구체적인 물리적 배치 : 사용하는 가비지 컬렉션에 따라 달라질 수 있음
핵심 정리
- Young Generation은 일반적으로 Eden과 두 개의 Survivor 영역으로 구성됩니다.
- 대부분의 새 객체는 Eden에서 시작합니다.
- TLAB은 Eden의 일부를 스레드별 객체 할당 영역으로 사용하는 구조입니다.
- 각 스레드는 자신의 TLAB에서 범프 포인터 방식으로 객체를 빠르게 할당할 수 있습니다.
- 모든 객체가 반드시 TLAB에 할당되는 것은 아닙니다.
- Eden에 객체를 할당할 공간이 부족해지면 Young GC가 발생할 수 있습니다.
- Young GC는 Eden과 From Survivor의 객체를 대상으로 생존 여부를 확인합니다.
- 살아남은 객체는 To Survivor로 복사되거나 Old Generation으로 승격됩니다.
- GC Root에서 도달할 수 없는 객체가 차지한 공간은 회수됩니다.
- 두 Survivor 영역은 From과 To 역할을 번갈아 수행합니다.
- 전통적인 Young Generation 모델에서는 생존 객체를 To Survivor나 Old Generation으로 이동시키고, 기존 Eden과 From Survivor를 다시 사용합니다.
- 객체가 Young GC에서 살아남으면 객체 나이가 증가할 수 있습니다.
- 여러 번 살아남거나 Survivor 공간이 부족한 경우 객체가 Old Generation으로 승격될 수 있습니다.
- Young GC 중 객체 복사와 참조 갱신을 위해 STW가 발생할 수 있습니다.
- 구체적인 Young Generation 구현은 사용하는 가비지 컬렉터에 따라 달라질 수 있습니다.
마무리
Young Generation에서는 객체를 빠르게 만들고, 짧게 살아남는 객체를 효율적으로 제거하기 위한 구조를 함께 사용합니다.
객체 생성 단계에서는 TLAB을 사용해 여러 스레드가 각자의 공간에서 빠르게 객체를 할당합니다.

Eden에 객체를 할당할 공간이 부족해지면 Young GC가 발생합니다.
가비지 컬렉터는 Eden과 From Survivor에서 살아남은 객체를 찾아 To Survivor로 복사하거나 Old Generation으로 승격합니다.

이 흐름의 핵심은 다음과 같습니다.
- TLAB : 새 객체를 빠르게 할당
- Eden : 대부분의 새 객체가 시작하는 영역
- Young GC : 생존 객체와 가비지 객체를 구분
- From / To Survivor : 생존 객체를 복사하며 번갈아가며 사용
- Promotion : 오래 살아남은 객체를 Old Generation으로 이동
객체 할당과 생존 객체 복사가 연결되면서 Young Generation은 짧은 수명의 객체를 빠르게 처리하고, 오래 살아남는 객체만 다음 세대로 보내는 구조를 완성합니다.
