STW
GC가 사용하지 않는 객체를 정리할 때 모든 애플리케이션 스레드를 일시적으로 멈추는 현상입니다.
왜 멈춰야 하는가
[ GC 없이 그냥 정리하면? ]
스레드 A: 객체 X를 사용 중 ──→ "X.name 읽어야지"
GC: 객체 X를 회수 ──→ "X 안 쓰니까 삭제!"
↑ 동시에 일어나면 크래시
[ 해결: 모두 멈추고 정리 ]
스레드 A: 멈춤 ─────────────────── 재개
스레드 B: 멈춤 ─────────────────── 재개
GC: 안전하게 객체 정리
GC가 객체를 옮기거나 삭제하는 동안 다른 스레드가 그 객체를 참조하면 데이터 불일치나 크래시가 발생합니다. 이를 방지하기 위해 모든 스레드를 멈추는 것입니다.
동작 과정
정상 실행 STW 정상 실행
────────── ────────────── ──────────
스레드 A: 작업 중 → 정지 ─────────── → 재개
스레드 B: 작업 중 → 정지 → 재개
스레드 C: 작업 중 → 정지 → 재개
│
GC 스레드만 동작
│
┌──┴──────────┐
│ 살아있는 객체 │
│ 마킹 │
│ 죽은 객체 제거│
│ 메모리 정리 │
└─────────────┘
체감 예시
[ 웹 서버에서 STW 발생 시 ]
사용자 요청 ──→ 서버 처리 중 ──→ STW 발생 (200ms)
│
▼
모든 요청 처리가 멈춤
사용자: "왜 갑자기 느려졌지?"
│
▼
STW 종료 → 밀린 요청 한꺼번에 처리
응답 시간
▲
│ ┌── STW 발생
│ 200ms│
│ ──────┤
│ 10ms │ 10ms
│ ──────┘──────── 정상
└───────────────────────► 시간
평소 10ms 응답이 STW 때문에 갑자기 200ms로 튐
STW가 발생하는 GC 단계
┌─── Young Gen ──────┐ ┌─── Old Gen ──────┐
│ │ │ │
│ Minor GC │ │ Major GC │
│ Eden이 가득 찰 때 │ │ Old 영역 부족 시 │
│ STW: 수 ms │ │ STW: 수십~수백 ms │
│ 빈번하지만 짧음 │ │ 드물지만 길음 │
└─────────────────────┘ └───────────────────┘
Full GC
Heap 전체 정리
STW: 수백 ms ~ 수 초
가장 치명적
| GC 종류 | 대상 | STW 시간 | 빈도 |
|---|---|---|---|
| Minor GC | Young Gen | 수 ms | 자주 |
| Major GC | Old Gen | 수십~수백 ms | 가끔 |
| Full GC | Heap 전체 | 수백 ms ~ 수 초 | 드묾 (발생 시 치명적) |
GC 알고리즘별 STW 비교
| GC | STW 방식 | 정지 시간 | 특징 |
|---|---|---|---|
| Serial | 전체 정지 | 수백 ms | 단일 스레드, 소규모 앱용 |
| Parallel | 전체 정지 | 수백 ms | 멀티 스레드로 빠르게 처리 |
| G1 | 부분 정지 | 수십 ms | 목표 정지 시간 설정 가능 |
| ZGC | 거의 동시 처리 | 수 ms 이하 | TB급 힙에서도 짧은 정지 |
| Shenandoah | 거의 동시 처리 | 수 ms 이하 | ZGC와 유사 |
STW 시간
▲
│
│ ███████████ Serial (수백 ms)
│ ██████████ Parallel (수백 ms)
│ ███ G1 (수십 ms)
│ █ ZGC (수 ms 이하)
│ █ Shenandoah (수 ms 이하)
└──────────────────────────────────────
Concurrent GC는 어떻게 STW를 줄이는가
ZGC, Shenandoah 같은 최신 GC는 애플리케이션과 동시에 정리합니다.
[ 전통적 GC (Serial, Parallel) ]
앱 스레드: 실행 ─── 전체 정지 ─────────── 재개
GC 스레드: 마킹 → 정리 → 압축
[ Concurrent GC (ZGC, Shenandoah) ]
앱 스레드: 실행 ─ 짧은 정지 ─ 실행 ─── 짧은 정지 ─ 실행
GC 스레드: 초기 마킹 동시 마킹 최종 마킹 동시 정리
(~1ms) (앱과 동시) (~1ms) (앱과 동시)
완전히 STW를 없앤 것이 아니라, 아주 짧은 STW만 남기고 대부분을 동시에 처리하는 방식입니다.
STW가 치명적인 분야
| 분야 | 영향 | 대안 |
|---|---|---|
| 실시간 트레이딩 | 수 ms 지연 = 수억 원 손실 | C++, Rust 사용 또는 GC 튜닝 |
| 온라인 게임 | 프레임 드랍, 끊김 | C++ 엔진 + 스크립트만 VM |
| 자율주행 | 정지 중 판단 불가 | C, C++ 사용 |
| 의료 장비 | 실시간 모니터링 중단 | C, Ada 사용 |
실무에서 STW를 줄이는 방법
| 전략 | 설명 |
|---|---|
| GC 알고리즘 변경 | -XX:+UseZGC로 ZGC 사용 |
| 힙 크기 적절 설정 | 너무 크면 Full GC 시 오래 정지 |
| 객체 생성 최소화 | 불필요한 new 줄이기, 객체 풀링 |
| GC 로그 모니터링 | -Xlog:gc*로 STW 시간 추적 |
| Young Gen 비율 조정 | Minor GC 빈도와 Major GC 빈도 균형 |
# ZGC 사용 + GC 로그 활성화
java -XX:+UseZGC -Xlog:gc* -jar app.jar
핵심 정리
Stop-The-World는 GC가 안전하게 메모리를 정리하기 위해 모든 애플리케이션 스레드를 일시 정지시키는 현상입니다. 전통적 GC에서는 수백 ms까지 멈출 수 있지만, ZGC·Shenandoah 같은 최신 GC는 STW를 수 ms 이하로 줄였습니다. 다만 완전 제거는 불가능하므로, 극한의 실시간 성능이 필요한 분야에서는 여전히 GC 없는 네이티브 언어(C, C++, Rust)를 선택합니다.