자바 가비지 컬렉션(GC) 원리 — 면접 완벽 정리
자바 가비지 컬렉션(GC) 원리, 면접에서 자주 묻는 JVM 질문
자바 가비지 컬렉션(GC) 원리는 자바 백엔드 면접의 핵심 단골 질문입니다. GC는 JVM이 더 이상 사용되지 않는(참조되지 않는) 객체를 자동으로 찾아 Heap 메모리를 회수하는 메커니즘입니다. 개발자가 직접 메모리를 해제하지 않아도 되는 자바의 핵심 장점이지만, 동작 원리를 모르면 성능 튜닝을 할 수 없습니다.
GC의 핵심 전제: 도달 가능성(Reachability)
GC는 객체가 'GC Root에서 참조로 도달 가능한가'를 기준으로 살아있는 객체와 쓰레기를 구분합니다. GC Root에는 스택의 지역 변수, 정적(static) 변수, JNI 참조 등이 포함됩니다. Root에서 참조 체인을 따라 도달할 수 없는 객체는 쓰레기로 간주되어 회수 대상이 됩니다. (참조 카운팅과 달리 순환 참조도 회수 가능합니다.)
Heap의 세대(Generational) 구조
GC는 약한 세대 가설(Weak Generational Hypothesis), 즉 '대부분의 객체는 금방 죽는다'는 경험칙에 기반합니다. 그래서 Heap을 세대로 나눕니다.
| 영역 | 설명 |
|---|---|
| Young Generation | 새로 생성된 객체. Eden + Survivor 0/1로 구성 |
| Old Generation | Young에서 오래 살아남은 객체가 승격(promotion) |
| Metaspace | 클래스 메타데이터(Java 8부터 PermGen 대체, 네이티브 메모리) |
Minor GC와 Major GC
- Minor GC: Young 영역이 가득 차면 발생. 빠르고 빈번. 살아남은 객체는 Survivor로 이동하며 age가 오름.
- Major(Full) GC: Old 영역 대상. 느리고 Stop-The-World 시간이 김. 일정 age를 넘으면 Old로 승격됨.
GC 알고리즘: Mark-Sweep-Compact
1. Mark : GC Root부터 참조를 따라가며 살아있는 객체에 표시
2. Sweep : 표시되지 않은 (도달 불가) 객체의 메모리 회수
3. Compact: 살아남은 객체를 한쪽으로 모아 단편화(fragmentation) 해소Stop-The-World(STW)
GC가 수행되는 동안 애플리케이션 스레드가 멈추는 현상을 STW라고 합니다. 마킹 등의 작업 중 객체 그래프가 바뀌면 안 되기 때문입니다. GC 튜닝의 목표는 결국 이 STW 시간을 줄이는 것입니다. STW가 길면 응답 지연(latency)이 튀게 됩니다.
대표 GC 종류
- Serial GC: 단일 스레드. 작은 애플리케이션용.
- Parallel GC: 여러 스레드로 GC 수행. 처리량(throughput) 중시. Java 8 기본.
- G1 GC: Heap을 균등한 Region으로 나눠 쓰레기가 많은 영역부터 회수. 예측 가능한 STW 목표. Java 9부터 기본.
- ZGC / Shenandoah: 대용량 Heap에서 STW를 수 밀리초 이하로 억제하는 저지연 GC.
면접 답변 예시
"가비지 컬렉션은 JVM이 더 이상 참조되지 않는 객체를 자동으로 회수하는 기능입니다. GC Root에서 참조로 도달 가능한지를 기준으로 살아있는 객체를 판단하고, 도달 불가능한 객체를 회수합니다. Heap은 약한 세대 가설에 따라 Young과 Old로 나뉩니다. 새 객체는 Young의 Eden에 생기고, Minor GC에서 살아남으면 Survivor를 거쳐 일정 age 이상이면 Old로 승격됩니다. 알고리즘은 기본적으로 Mark-Sweep-Compact를 거치며, 이때 애플리케이션이 멈추는 Stop-The-World가 발생합니다. GC 튜닝의 핵심은 이 STW 시간을 줄이는 것이고, 그래서 G1, ZGC 같은 저지연 GC가 발전해 왔습니다."
면접 꼬리질문 대비
Q1. GC가 있는데도 메모리 누수가 생기나요?
생깁니다. GC는 '참조되지 않는' 객체만 회수하므로, 더 이상 필요 없지만 어딘가에서 계속 참조하고 있는 객체는 회수되지 않습니다. 대표적으로 정적 컬렉션(static List/Map)에 객체를 계속 넣고 안 빼는 경우, 닫지 않은 리소스, 잘못 등록된 리스너 등이 누수를 유발합니다.
Q2. Survivor 영역이 0, 1 두 개인 이유는?
Minor GC 때 살아남은 객체를 한 Survivor에서 다른 Survivor로 옮기며 Compact(단편화 해소)를 효율적으로 하기 위해서입니다. 항상 하나는 비어 있고, 살아남은 객체를 비어 있는 쪽으로 모아 옮긴 뒤 역할을 교대합니다. 이를 통해 복사 알고리즘으로 빠르게 살아있는 객체만 추려냅니다.
Q3. G1 GC가 기존 GC와 다른 점은?
기존 GC는 Young/Old를 물리적으로 연속된 큰 영역으로 다뤘지만, G1은 Heap을 동일 크기의 작은 Region들로 나누고 각 Region을 논리적으로 Eden/Survivor/Old로 할당합니다. 그리고 쓰레기 비율이 높은 Region(Garbage First)부터 우선 수거해, 목표한 STW 시간 안에서 최대 효율로 회수합니다. 대용량 Heap에서 예측 가능한 지연이 장점입니다.