이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • GC 기본과 Pause 이해에서 붙잡아야 할 핵심 판단 기준은 무엇인가?
  • GC는 회수와 pause의 trade-off다라는 기준은 어떤 JVM/OS 신호로 확인되는가?
  • Java/Spring 코드에서 GC 기본과 Pause 이해 판단 기준은 어떤 장애 신호로 드러나는가?

개요

이 문서의 주제는 GC 기본과 Pause 이해이다. GC의 기본 목적, stop-the-world, pause, allocation pressure를 백엔드 latency와 연결한다.

먼저 붙잡을 기준은 다음 문장이다. GC는 회수와 pause의 trade-off다. GC는 memory를 회수해 서버를 계속 살리지만, 일부 구간에서는 application thread를 멈추고 CPU를 사용해 API latency에 직접 영향을 준다.

핵심 모델

  • 판단 기준: GC는 회수와 pause의 trade-off다.
  • GC pause는 application thread가 멈추는 시간이므로 latency SLO와 직접 연결된다.
  • Allocation rate가 높으면 young GC가 잦아지고, 오래 살아남는 객체가 많으면 old 영역 압박이 커진다.
  • GC tuning보다 먼저 allocation 원인, object lifetime, after-GC heap baseline을 확인해야 한다.
  • 평균 pause보다 p95/p99, max pause, pause 발생 시간대가 중요하다.
  • GC log와 Actuator jvm.gc.pause는 API latency와 같은 시간축에서 봐야 한다.

문서별 핵심 구분

  • GC pause는 application thread가 멈추는 시간으로 latency에 직접 영향을 준다.
  • allocation rate와 object lifetime이 GC 압박을 만든다.
  • GC 종류 변경보다 allocation 원인 파악이 먼저다.

원리

GC는 더 이상 참조되지 않는 객체를 회수한다. 문제는 회수 작업 자체가 CPU와 pause를 만들고, 일부 collector phase는 application thread를 멈춘다는 점이다.

GC pause가 길어지면 Tomcat worker thread가 정상이어도 request latency가 튈 수 있다. 이때 API log만 보면 특정 endpoint가 느린 것처럼 보이지만 실제 원인은 같은 시간대의 stop-the-world일 수 있다.

Allocation rate가 높은 코드는 GC를 자주 부른다. 큰 JSON 응답, 대량 DTO 변환, 반복 문자열 생성, 과도한 logging, batch list 적재가 대표 후보가 된다.

따라서 GC 문제는 “GC가 많다”가 아니라 allocation rate, after-GC heap baseline, pause 분포, API latency가 같은 시각에 어떻게 움직였는지로 판단한다.

Java/Spring 연결

  • JSON 직렬화, DTO mapping, validation, logging은 request당 allocation rate를 크게 만들 수 있다.
  • Actuator jvm.gc.pause, jvm.memory.used, jvm.gc.memory.allocated 계열 지표는 latency와 함께 봐야 한다.
  • @Async나 batch 작업이 같은 heap을 쓰면 사용자 요청과 GC 비용을 공유한다.
  • GC pause가 늘면 thread pool busy가 낮아도 request timeout이 날 수 있다.
  • GC option 변경보다 어떤 코드 경로가 객체를 많이 만드는지 JFR allocation profile로 확인하는 것이 먼저다.

코드와 설정 예시

jcmd <pid> GC.heap_info
curl -s localhost:8080/actuator/metrics/jvm.gc.pause
jcmd <pid> JFR.start name=gc settings=profile duration=120s filename=gc.jfr

예시는 heap 상태, GC pause metric, 짧은 JFR을 같은 시간대에 남기는 흐름이다. GC log가 켜져 있다면 API latency spike와 pause timestamp를 맞춰 본다.

관찰 명령

jcmd <pid> GC.heap_info
jcmd <pid> VM.flags
curl -s localhost:8080/actuator/metrics/jvm.gc.pause

GC 기본과 Pause 이해 관찰 명령은 결론이 아니라 가설 확인 도구다. 운영 중에는 반복 간격, 대상 PID, 수집 시각을 함께 남기고 출력이 큰 명령은 범위를 제한한다.

지표 해석

신호먼저 의심할 것다음 확인
Pause p99 증가stop-the-world 영향GC log, latency spike
Young GC 빈도 증가allocation rate 상승JFR allocation, endpoint
After-GC old baseline 상승leak 또는 오래 사는 객체 증가heap dump, class histogram
GC CPU 증가collector work 증가CPU, GC log, allocation burst

GC 분석에서는 allocation burst, pause 길이, after-GC baseline, latency spike의 순서를 맞춰야 GC가 원인인지 다른 지연의 결과인지 분리할 수 있다.

장애 상황에서 보는 순서

  1. API latency spike와 GC pause timestamp를 맞춘다.
  2. Pause p95/p99/max와 GC 빈도를 나눠 본다.
  3. After-GC heap baseline이 상승하는지 확인한다.
  4. Allocation rate가 높은 endpoint나 batch 작업을 JFR로 찾는다.
  5. GC option 변경 전에는 코드 allocation 원인과 heap 크기 근거를 남긴다.

이 순서는 GC를 무작정 튜닝하지 않기 위한 루틴이다. pause와 latency가 겹친다는 사실과 GC option 변경이 답이라는 결론은 별도 증거가 필요하다.

실전 팁

  • GC pause는 평균보다 p95/p99와 max pause를 SLO에 맞춰 본다.
  • Young GC 빈도는 allocation rate의 신호일 수 있다.
  • After-GC old 영역이 계속 올라가면 leak 또는 오래 사는 객체를 의심한다.
  • GC collector 변경은 마지막 수단에 가깝고, 먼저 객체 생성 경로를 줄인다.
  • 장애 회고에는 pause timestamp와 latency spike를 함께 남긴다.

주니어 팁

  • GC는 나쁜 것이 아니라 필요한 회수 작업이라는 점을 먼저 이해한다.
  • Pause가 문제인지, 빈도가 문제인지, after-GC baseline이 문제인지 나눠 본다.
  • API latency와 GC pause 시간대가 겹치는지 확인한다.

시니어 팁

  • Allocation budget을 endpoint와 batch 작업 단위로 관리한다.
  • GC log는 장애가 난 뒤 켜기보다 운영 기본 관찰 정책에 포함하는 편이 낫다.
  • Heap 크기와 collector 선택은 latency SLO, container limit, allocation profile 근거로 결정한다.

Guru급 팁

  • Endpoint별 allocation budget을 두면 GC 문제를 collector 논쟁이 아니라 코드 경로 비용으로 관리할 수 있다.
  • GC log는 pause 원인, heap before/after, promotion, concurrent phase를 함께 보존해야 장애 후 재현 없이도 판단할 수 있다.
  • Collector 변경은 latency SLO, heap 크기, allocation profile, container CPU quota를 함께 놓고 실험해야 한다.

위험 신호!

  • 평균 pause만 보고 p99/max pause를 보지 않는다.
  • GC가 잦다는 이유로 collector부터 바꾼다.
  • Allocation rate 높은 코드 경로를 찾지 않는다.
  • Heap만 키워 leak이나 batch 적재 문제를 숨긴다.
  • GC log 없이 GC tuning을 시도한다.

확인 질문

확인 질문

  • GC pause가 API latency에 영향을 주는 이유는 무엇인가?
    • Stop-the-world 구간에서는 application thread가 멈춰 요청 처리가 지연될 수 있기 때문이다.
  • GC 빈도 증가와 pause 증가를 구분해야 하는 이유는 무엇인가?
    • 빈도는 allocation rate 문제일 수 있고, 긴 pause는 heap 압박이나 old 영역 문제일 수 있어 조치가 다르기 때문이다.
  • GC option 변경 전 먼저 확인할 것은 무엇인가?
    • GC log, pause 분포, after-GC heap baseline, allocation rate, latency spike 시간대를 확인해야 한다.

참고 문서