F-lab Java 1주차 / Phase 5 / Unit 5.4 본격 학습 자료 — Phase 5 마무리!
9-섹션 마스터 프롬프트 형식으로 깊이 파헤친다.선수 지식: Unit 5.3 (GC 알고리즘 4가지)
다음 Phase: Phase 6 — 데이터 다루기 (문자열·컬렉션)이 Unit의 의미: 운영 환경에서 GC 를 선택하는 실용 가이드.
Serial, Parallel, CMS, G1, ZGC, Shenandoah 의 정확한 차이.
ILIC 운영에 어떤 GC 가 맞는지 결정 가능.
큰 사무실의 청소 서비스 옵션:
→ 이게 자바 GC 의 6가지 종류.
"GC 는 알고리즘이 같아도 구현 (멀티 스레드, Concurrent, Region 기반) 에 따라 다양한 종류로 진화했다."
각 GC 의 trade-off:
비유 정리:
| 비유 | GC 종류 | 적합 |
|---|---|---|
| 1인 청소부 | Serial | 작은 환경 |
| 청소 팀 | Parallel | 처리량 우선 |
| 동시 청소 | CMS | 응답성 (deprecated) |
| 구역별 청소 | G1 | 일반 (기본) |
| 멈춤 없는 청소 | ZGC | 저지연 |
GC 의 핵심 trade-off:
[처리량 (Throughput)] ←——————→ [응답성 (Latency)]
배치, 대량 처리 사용자 응답
STW 길어도 OK STW 짧아야
한 가지 GC 로는 모든 환경 만족 불가:
→ 다양한 GC 가 등장.
자바 버전마다 기본 GC 가 진화:
| 자바 버전 | 기본 GC |
|---|---|
| 1.0~1.4 | Serial |
| 1.5~1.7 | Parallel (Heap 큰 경우) |
| 8 | Parallel ⭐ |
| 9~ | G1 ⭐ |
| 11~ | G1 (ZGC 실험적) |
| 15~ | G1 (ZGC 정식) |
| 17~ | G1 (Generational ZGC 실험적) |
| 21~ | G1 (Generational ZGC 정식) |
박승제님의 Java 17 환경:
→ 큰 Heap 에서도 효율적인 GC 필요.
"GC 종류 선택은 '내 환경의 우선순위' 의 답이다."
처리량인가 응답성인가? 작은 Heap 인가 큰 Heap 인가? 단일 CPU 인가 멀티 CPU 인가?
모든 환경에 맞는 단일 GC 는 없다. 상황에 맞게 선택. 시니어 개발자라면 각 GC 의 특성을 알고 환경에 맞는 선택 가능해야.
GC 종류를 모르면 운영 환경에서 다양한 문제 발생.
java -jar ilic.jar # 기본 GC (Java 17 → G1)
알고 사용: 의도적 G1 선택 ✅
모르고 사용: 어떤 GC 인지도 모름 ❌
문제 시:
java -XX:+UseG1GC -Xmx512m -jar small.jar
문제:
java -XX:+UseParallelGC -jar webapp.jar
문제:
ILIC 서비스:
- Heap: 4GB
- API 응답 시간 SLA: P99 < 500ms
- 사용 GC: 모름 (기본)
문제:
가끔 응답 시간 2초 → SLA 위반
GC 종류 모르면:
GC 종류 알면:
"ILIC 에서 어떤 GC 사용했고, 왜 그 GC 인가요?"
답 못함:
잘 답함:
문제: Full GC 빈번, STW 김
GC 종류 모르면:
GC 종류 알면:
MaxGCPauseMillis)Java 8 → Java 17 마이그레이션
기본 GC 변경: Parallel → G1
GC 종류 모르면:
GC 종류 알면:
| 시나리오 | GC 종류 모르면 | GC 종류 알면 |
|---|---|---|
| 기본 GC 사용 | 무지식 | 의도 명확 |
| GC 선택 | 잘못 선택 | 상황에 맞는 선택 |
| 운영 분석 | 막막함 | 정확한 진단 |
| 면접 | 탈락 | 시니어 인식 |
| 튜닝 | 옵션 모름 | 다양한 선택지 |
| 버전 업그레이드 | 당황 | 계획적 |
→ GC 종류 이해는 시니어 자바 개발자의 필수.
java -XX:+UseSerialGC -jar app.jar
java -XX:+UseParallelGC -jar app.jar
# 처리량 목표
-XX:GCTimeRatio=99 # 1% 이내 GC
-XX:MaxGCPauseMillis=200 # 목표 STW
java -XX:+UseConcMarkSweepGC -jar app.jar # ❌ Java 14+에서 사용 불가
[Heap]
├── Region 0 (Eden)
├── Region 1 (Survivor)
├── Region 2 (Old)
├── Region 3 (Humongous)
├── Region 4 (Free)
└── ... 수백~수천 Region
MaxGCPauseMillis)java -XX:+UseG1GC -jar app.jar
# 주요 튜닝 옵션
-XX:MaxGCPauseMillis=200 # 목표 STW (기본 200ms)
-XX:G1HeapRegionSize=4m # Region 크기 (자동 계산도 OK)
-XX:InitiatingHeapOccupancyPercent=45 # Mixed GC 트리거
-XX:ConcGCThreads=2 # Concurrent 스레드 수
java -XX:+UseZGC -jar app.jar
# Generational ZGC (Java 21+)
java -XX:+UseZGC -XX:+ZGenerational -jar app.jar
java -XX:+UseShenandoahGC -jar app.jar
| GC | 등장 | 알고리즘 | STW | 처리량 | 적합 환경 |
|---|---|---|---|---|---|
| Serial | 1.0 | 단일 스레드 | 김 | 보통 | 작은 환경, 단일 CPU |
| Parallel | 1.4 | 멀티 스레드 | 김 (단축) | 높음 | 처리량 우선, 배치 |
| CMS | 1.4 | Concurrent | 짧음 | 보통 | (deprecated) |
| G1 | 7+, 9 default | Region | 보통 | 높음 | 일반 백엔드 ⭐ |
| ZGC | 11+ | Colored Pointer | 매우 짧음 | 보통 | 저지연, 큰 Heap |
| Shenandoah | 12+ | Red Hat | 매우 짧음 | 보통 | 저지연, OpenJDK |
Heap 크기?
├── ~100MB
│ └── Serial (단순, 효율)
│
├── 100MB ~ 4GB
│ ├── 처리량 우선?
│ │ └── Parallel
│ └── 응답성 우선?
│ └── G1
│
├── 4GB ~ 32GB
│ └── G1 (기본 권장) ⭐
│
└── 32GB+
└── 응답성 매우 중요?
├── YES: ZGC
└── NO: G1
Case 1: 일반 API 서버 (Heap 2~4GB):
java -XX:+UseG1GC \
-Xms2g -Xmx2g \
-XX:MaxGCPauseMillis=200 \
-jar ilic.jar
Case 2: 배치 처리 (대용량 데이터):
java -XX:+UseParallelGC \
-Xms8g -Xmx8g \
-jar batch.jar
Case 3: 실시간 시스템 (저지연):
java -XX:+UseZGC \
-Xms16g -Xmx16g \
-jar realtime.jar
1. STW 시작 (모든 Application Thread 정지)
2. 단일 GC 스레드가 Mark + Sweep + Compact
3. STW 종료
→ 단순. 작은 Heap 에서는 오히려 빠를 수 있음.
1. STW 시작
2. 여러 GC 스레드가 병렬로 Mark + Sweep + Compact
3. STW 종료
스레드 수:
기본: CPU 코어 수
조정: -XX:ParallelGCThreads=N
→ STW 시간 = Serial 의 1/N (N = 스레드 수).
[G1 Heap]
├── Eden Region (여러 개)
├── Survivor Region (여러 개)
├── Old Region (여러 개)
├── Humongous Region (큰 객체)
└── Free Region (사용 가능)
1. Young GC (Minor GC):
2. Concurrent Marking:
3. Mixed GC:
4. Full GC (피해야 할):
[Heap]
├── Region A: 80% Garbage (target)
├── Region B: 30% Garbage
├── Region C: 90% Garbage (target)
├── Region D: 10% Garbage
└── ...
GC 우선 청소: Region A, C
→ 가장 효율적인 Region 부터 청소.
-XX:MaxGCPauseMillis=200:
일반 64-bit 포인터:
[ 64 bits (주소) ]
ZGC 의 Colored Pointer:
[ 4 bits (color) ][ 60 bits (주소) ]
Color 의미:
- Marked0: Live (이번 사이클)
- Marked1: Live (다음 사이클)
- Remapped: 재배치됨
- Finalizable: finalize 대상
→ 포인터 자체에 GC 정보 저장.
// 사용자 코드
Customer c = customers.get(0); // 객체 참조 시
// JVM 내부 (Load Barrier)
- 포인터 색상 확인
- 필요 시 동시 마킹
- 필요 시 재배치 처리
→ 매 참조 시 약간의 작업, 그러나 STW 거의 없음.
1. Pause Mark Start (~1ms)
- GC Root 마킹
2. Concurrent Marking
- Application 과 동시 실행
- Heap 전체 마킹
3. Pause Mark End (~1ms)
- 마킹 마무리
4. Concurrent Relocation
- Live 객체 재배치
- Application 과 동시
→ STW 는 수 ms 만. Heap 크기 무관.
| G1 | ZGC | |
|---|---|---|
| Region 크기 | 1~32MB | ~수 GB |
| STW | ~200ms (목표) | ~10ms 이하 |
| Heap | 4GB~수백 GB | 수 TB 가능 |
| CPU 사용 | 보통 | 약간 ↑ |
| 메모리 사용 | 보통 | 약간 ↑ |
| 처리량 | 높음 | 약간 ↓ |
| 적합 | 일반 서버 | 저지연 |
Concurrent GC 의 장점:
Concurrent GC 의 단점:
→ 응답성을 위해 다른 자원 더 사용.
# 방법 1: PrintCommandLineFlags
java -XX:+PrintCommandLineFlags -version
# 출력 예시:
# -XX:+UseG1GC ← 사용 중인 GC
# 방법 2: GC 로그
java -Xlog:gc -jar myapp.jar
# 출력에서 GC 이름 확인
# 일반 백엔드 (Heap 2~4GB)
java -XX:+UseG1GC \
-Xms2g -Xmx2g \
-XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/heap-dump.hprof \
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags \
-jar ilic.jar
public class GCBenchmark {
public static void main(String[] args) {
long start = System.currentTimeMillis();
List<byte[]> survivors = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
byte[] data = new byte[1024]; // 1KB
if (i % 1000 == 0) {
survivors.add(data);
}
}
long elapsed = System.currentTimeMillis() - start;
System.out.println("Total time: " + elapsed + "ms");
}
}
다른 GC 로 실행:
# Serial
java -XX:+UseSerialGC GCBenchmark
# 출력: ~3000ms (단일 스레드)
# Parallel
java -XX:+UseParallelGC GCBenchmark
# 출력: ~1500ms (병렬, 처리량 ↑)
# G1
java -XX:+UseG1GC GCBenchmark
# 출력: ~2000ms (균형)
# ZGC
java -XX:+UseZGC GCBenchmark
# 출력: ~2200ms (응답성, 처리량 약간 ↓)
→ GC 선택이 성능에 직결.
# 운영 환경 — 검증된 설정
java \
# GC 알고리즘
-XX:+UseG1GC \
# Heap 크기 (고정 권장)
-Xms4g -Xmx4g \
# 목표 STW
-XX:MaxGCPauseMillis=200 \
# OOM 시 자동 덤프
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/heap-dump-%t.hprof \
# GC 로그
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags:filecount=10,filesize=100M \
# 추가 모니터링
-XX:+PrintTenuringDistribution \
-XX:+UnlockExperimentalVMOptions \
-jar ilic.jar
박승제님이 Java 17 사용 시:
java \
-Xms2g -Xmx2g \
-XX:MaxGCPauseMillis=200 \
-Xlog:gc*:file=/var/log/gc.log \
-jar ilic.jar
→ 기본 G1 사용. 명시적 UseG1GC 옵션 불필요.
저지연 필요 시:
# Java 17+ 의 ZGC
java \
-XX:+UseZGC \
-Xms8g -Xmx8g \
-Xlog:gc*:file=/var/log/gc.log \
-jar ilic.jar
Java 21 이상:
# Generational ZGC (더 효율적)
java \
-XX:+UseZGC \
-XX:+ZGenerational \
-Xms8g -Xmx8g \
-jar ilic.jar
# 1. JConsole
jconsole <pid>
# 2. VisualVM
visualvm
# 3. JFR (Java Flight Recorder)
java -XX:StartFlightRecording=duration=60s,filename=recording.jfr -jar app.jar
# 4. JMC (Java Mission Control) 로 분석
jmc
# 5. GC 로그 분석 도구
# - GCViewer (오픈소스)
# - gceasy.io (웹 서비스)
java -jar myapp.jar # 무조건 기본
문제:
권장: 상황에 맞는 GC 명시적 선택.
java -XX:+UseConcMarkSweepGC -jar app.jar # ❌ Java 14+에서 제거
대체:
"저지연 = ZGC = 좋다"
진실:
원칙: Heap 크기 + 우선순위 에 맞게.
java -Xmx8g -jar myapp.jar
누락:
-Xms (초기 Heap) — 초기 작으면 동적 확장 비용-Xlog:gc (로그) — 분석 불가-XX:+HeapDumpOnOutOfMemoryError — 사고 시 덤프 X권장 최소 옵션:
java -Xms4g -Xmx4g \
-Xlog:gc*:file=/var/log/gc.log \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/ \
-jar myapp.jar
java -Xms512m -Xmx4g -jar myapp.jar
문제:
권장: Xms == Xmx (동일 값)
java -Xms4g -Xmx4g -jar myapp.jar
운영 환경:
해결: 항상 GC 로그 활성화.
java -XX:+UseG1GC -XX:G1HeapRegionSize=64m -Xmx2g -jar app.jar
문제:
권장: G1 자동 계산 사용 (옵션 생략).
[Unit 5.1: GC 기본 + 약한 세대 가설] ✓
↓
[Unit 5.2: Heap 의 세대 구조] ✓
↓
[Unit 5.3: GC 알고리즘 4가지] ✓
↓
[Unit 5.4: GC 종류와 선택 기준] ← 지금 여기 ★
↓
[Phase 5 완료!] 🎉
→ Phase 5 완료. 다음은 Phase 6 데이터 다루기.
| 학습 | GC 선택 관점 |
|---|---|
| Unit 4.1 (Heap 영역) | GC 의 무대 |
| Unit 4.2 (Pass by Value) | 객체 참조 → GC 영향 |
| Unit 5.1 (약한 세대) | Generational 의 기반 |
| Unit 5.2 (세대 구조) | G1 의 Region 이해 |
| Unit 5.3 (알고리즘) | GC 종류의 토대 |
4주차 (동시성):
5주차 (Spring DI):
11-12주차 (JPA):
13-14주차 (DB/Cache):
17주차 (MSA):
19주차 (테스트):
새 ILIC 서비스 시작
↓
Java 버전?
├── 8: Parallel 기본 (변경 권장 — G1)
└── 11+: G1 기본 (대부분 그대로)
↓
응답 시간 SLA?
├── 1초+ OK: G1 (기본)
├── 200ms 목표: G1 + MaxGCPauseMillis=200
└── 100ms 미만: ZGC 고려
↓
Heap 크기?
├── ~4GB: G1
├── 4~32GB: G1 (튜닝 가능)
└── 32GB+: ZGC 강력 권장
↓
Java 21+?
└── ZGC 사용 시 +ZGenerational
| 질문 | 이 Unit에서의 답 |
|---|---|
| "GC 종류?" | Serial, Parallel, CMS (deprecated), G1, ZGC, Shenandoah |
| "Java 8/9 기본 GC 변화?" | Parallel → G1 |
| "G1 vs ZGC?" | G1 = 균형, ZGC = 저지연 |
| "ILIC 에서 어떤 GC?" | "G1 사용했고, MaxGCPauseMillis=200" |
| "CMS 가 deprecated 된 이유?" | 단편화 + 복잡성 |
1️⃣ 자바 GC 는 6가지 — Serial, Parallel, CMS (deprecated), G1, ZGC, Shenandoah.
Serial = 단일 스레드 (작은 환경), Parallel = 멀티 스레드 (Java 8 기본, 처리량), CMS = Concurrent (Java 14에서 제거), G1 = Region 기반 (Java 9+ 기본, 일반 백엔드), ZGC = Colored Pointer (저지연, 큰 Heap), Shenandoah = Red Hat 의 ZGC 유사품. 모두 Generational + 추가 최적화.
2️⃣ G1 은 일반 백엔드의 표준, ZGC 는 저지연의 답.
G1 의 Region 기반 + Garbage First 전략으로 목표 STW 설정 (
MaxGCPauseMillis=200) 가능. 큰 Heap 에서도 효율적. ZGC 의 Colored Pointer + Load Barrier 로 STW 10ms 이하 (Heap 크기 무관). 응답성 vs 처리량/메모리의 trade-off — 박승제님의 Java 17 + ILIC 4GB Heap 환경이라면 G1 이 정답.3️⃣ GC 선택은 Heap 크기 + 우선순위의 답이다.
~100MB → Serial, ~4GB → G1 (또는 처리량 우선이면 Parallel), 4~32GB → G1, 32GB+ 또는 저지연 → ZGC. 운영 시 항상 GC 로그 활성화 + HeapDumpOnOutOfMemoryError + Xms=Xmx 권장. Java 14+ 에서 CMS 사용 시도는 X. 이런 명확한 기준으로 GC 선택할 수 있어야 시니어 자바 개발자.
Q1: G1 GC 가 큰 Heap 에 적합한 이유는?
한 줄 답: Region 기반 + Garbage First 전략 + 목표 STW 설정 가능 으로 큰 Heap 에서도 STW 짧게 유지.
상세 설명:
기존 (Parallel, Serial) GC:
[Heap 16GB]
├── Young (5GB)
└── Old (11GB)
Old GC (Major GC) 시:
큰 Heap = 긴 STW ⚠️.
[Heap 16GB]
├── Region 0 (4MB)
├── Region 1 (4MB)
├── Region 2 (4MB)
├── ... 4000개 Region
└── Region 3999
Region 의 의미:
GC 시 결정:
"어떤 Region 부터 청소할까?"
각 Region 의 Garbage 비율 분석:
- Region A: 80% Garbage
- Region B: 20% Garbage
- Region C: 90% Garbage
- Region D: 10% Garbage
선택: 효율 높은 순서
- Region C (90%)
- Region A (80%)
- Region B (20%) — 효율 낮음 → 스킵
효과:
-XX:MaxGCPauseMillis=200 # 목표 STW 200ms
G1 의 동작:
1. 200ms 안에 끝낼 수 있는 Region 수 계산
2. 그만큼만 청소
3. 다음 GC 에서 나머지 처리
예시:
→ 항상 200ms 안에 GC 완료.
G1 은 일부 작업을 Application Thread 와 동시 실행:
1. Initial Mark (STW, 짧음)
2. Concurrent Mark (Application 과 동시) ⭐
3. Remark (STW, 짧음)
4. Cleanup (Concurrent + 짧은 STW)
5. Mixed GC (Young + 일부 Old)
효과:
Heap 16GB, Old 가득:
Parallel GC:
Full GC:
- 16GB 전체 마킹 (수 초)
- 압축 (수 초)
- 총 STW: 5~10초 ⚠️
G1 GC:
Mixed GC:
- Concurrent Mark (수 초, 동시 실행)
- 200ms 만큼 Region 청소
- 부족하면 다음 GC 에서 더
- 총 STW: 200ms ✅
→ 큰 Heap 일수록 G1 의 효과 ↑.
G1 만의 특수 처리:
"G1 의 Region 기반 설계와 Garbage First 전략이 큰 Heap 에서 STW 를 짧게 유지.
Concurrent Marking 으로 Application 과 동시 작업 + 목표 STW 설정 으로 응답성 보장.
그래서 4GB 이상 큰 Heap 에 적합."
Q2: ZGC 와 G1 의 가장 큰 차이는?
한 줄 답: STW 길이의 차원이 다름 — G1 ~200ms vs ZGC ~10ms 이하.
상세 비교:
| G1 | ZGC | |
|---|---|---|
| 목표 STW | ~200ms | ~10ms 이하 |
| Heap 크기와 관계 | 어느 정도 비례 | 무관 |
| 큰 Heap 에서 | STW 길어짐 | STW 그대로 |
ZGC 의 마법 — Heap 이 100GB 든 1TB 든 STW 동일 (~10ms).
G1:
ZGC:
| G1 | ZGC | |
|---|---|---|
| 메모리 오버헤드 | 보통 | 약간 ↑ |
| CPU 사용 | 보통 | 약간 ↑ |
| 처리량 | 높음 | 약간 ↓ |
→ ZGC 는 응답성을 위해 다른 자원 더 사용.
G1:
ZGC:
G1:
ZGC:
G1 의 영역:
ZGC 의 영역:
G1 = 정기 청소:
ZGC = 24시간 청소부:
Case 1: 일반 ILIC API (Heap 4GB):
G1 ⭐
- 충분한 응답성 (200ms)
- 단순한 운영
- Java 17 기본
Case 2: ILIC 실시간 알림 (Heap 8GB):
G1 가능, ZGC 도 OK
- G1 + MaxGCPauseMillis=100 시도
- 부족하면 ZGC 전환
Case 3: ILIC 대용량 데이터 분석 (Heap 64GB):
ZGC ⭐
- 큰 Heap 에서 STW 안정
- 처리량 약간 손실 OK
"G1 = 일반 백엔드의 균형 (응답성 + 처리량),
ZGC = 저지연 시스템의 답 (응답성 최우선).
차이는 STW 의 차원 과 Heap 크기 의존성.
ILIC 같은 일반 백엔드는 G1 가 정답, 저지연 필요 시 ZGC 검토."
| Unit | 주제 | 핵심 |
|---|---|---|
| 5.1 | GC 기본 + 약한 세대 가설 ★★★ | Reachability, GC Root, STW |
| 5.2 | Heap 의 세대 구조 ★★★ | Eden/Survivor/Old, Promotion |
| 5.3 | GC 알고리즘 4가지 | RC, Mark-Sweep, MSC, Generational |
| 5.4 | GC 종류와 선택 기준 ★★★ | Serial/Parallel/G1/ZGC |
"GC 는 자바의 정수다. 세대 가설 → 세대 구조 → 알고리즘 → 종류 → 선택 — 모두 한 흐름."
약한 세대 가설이라는 통찰이 Heap 의 세대 구조를 만들고, 영역마다 다른 알고리즘을 적용하고, 각 환경에 맞는 GC 종류로 진화. Phase 4 + Phase 5 가 자바 메모리 관리의 완전한 이해.
다음은 자바에서 데이터를 다루는 방법:
→ 메모리 모델 (Phase 4-5) 이해 위에서 자료구조를 다룸.