본문 바로가기

C#

[GC편 #2] .NET GC 로그 분석 방법

들어가며

운영 환경에서 이런 경험을 해본 적이 있을 것입니다.

  • CPU 사용률은 낮은데 응답이 느리다
  • 특정 시간마다 서버가 멈춘 것처럼 보인다
  • 스레드는 충분한데 처리량이 떨어진다

이런 문제의 상당수는 GC(Garbage Collection) 와 관련되어 있으며, 이를 명확히 파악하는 가장 강력한 수단이 바로 GC 로그 분석입니다.

이 글에서는

  • GC 로그란 무엇인지
  • 어떻게 수집하는지
  • 로그에서 무엇을 봐야 하는지
  • 실제 문제를 어떻게 찾아내는지

를 실무 관점에서 차근차근 설명합니다.


1. GC 로그란 무엇인가?

GC 로그는 .NET 런타임이 메모리 수집 과정에서 발생한 모든 이벤트를 기록한 데이터입니다.

여기에는 다음 정보들이 포함됩니다.

  • GC 발생 시점
  • Gen 0 / 1 / 2 수집 여부
  • Stop-The-World 시간
  • 힙 크기 변화
  • LOH 사용량
  • Background GC 동작 여부

👉 즉, GC 로그는 메모리와 성능 문제의 블랙박스 기록입니다.


2. GC 로그 수집 방법

2.1 dotnet-trace (가장 간단한 방법)

dotnet-trace collect \
  --process-id 1234 \
  --providers Microsoft-Windows-DotNETRuntime:0x1C00008000000001
  • 실행 중인 프로세스에 바로 연결 가능
  • 운영 환경에서도 부담이 비교적 적음
  • 결과물: .nettrace 파일

2.2 PerfView (가장 강력한 도구)

PerfView는 GC 분석의 사실상 표준 도구입니다.

PerfView.exe collect /GCOnly /AcceptEULA
  • ETW 기반
  • 매우 상세한 GC 이벤트 제공
  • 힙, 스레드, CPU 연계 분석 가능

👉 서버 장애 분석에서는 PerfView 사용이 거의 필수입니다.


2.3 환경 변수 기반 로그 수집

set COMPlus_GCLogEnabled=1
set COMPlus_GCLogFile=gc.log
  • 애플리케이션 재시작 필요
  • 텍스트 기반 로그 생성
  • 컨테이너 환경에서 유용

3. GC 로그에서 반드시 봐야 할 핵심 지표

3.1 GC 빈도

  • Gen 0 GC가 초당 수십 번 발생 → 객체 생성 과다
  • Gen 2 GC가 잦음 → 장수 객체 누수 의심
GC(0) Pause 5ms
GC(2) Pause 350ms  ← 위험 신호

3.2 Stop-The-World 시간

  • 50ms 이하: 대부분 허용 가능
  • 100ms 이상: 사용자 체감 발생
  • 300ms 이상: 장애로 인식

멀티스레드 서버에서 긴 STW는 전체 요청 정체로 이어집니다.


3.3 힙 크기 증가 패턴

  • GC 후에도 힙이 줄지 않음
  • 지속적인 힙 확장

➡ 객체가 계속 살아남고 있다는 의미


3.4 LOH (Large Object Heap)

LOH Size: 1.2GB
  • LOH는 기본적으로 압축되지 않음
  • 로그에서 LOH가 커질수록 성능 악화

4. PerfView로 실제 분석하는 방법

4.1 GCStats 보기

  • 평균 GC 시간
  • 최대 GC 시간
  • 세대별 수집 횟수

👉 여기서 Max Pause Time이 가장 중요합니다.


4.2 Heap Stacks 분석

  • 어떤 메서드가 객체를 생성했는지
  • 어떤 타입이 힙을 점유하는지
System.Byte[]  ← 최상위 점유

➡ 버퍼 재사용, ArrayPool 필요 신호


4.3 Alloc Stacks

  • 스레드별 할당 패턴 확인
  • 특정 스레드에서 과도한 할당 발견 가능

5. 실제 장애 사례

사례: API 서버가 10분마다 멈춤

  • 증상: 응답 지연 1~2초 발생
  • 로그 분석 결과:
    • Gen 2 GC 발생 주기 10분
    • STW 800ms
    • LOH 2GB 이상

원인

  • 대형 byte[] 매 요청 생성
  • 캐시 객체가 해제되지 않음

해결

byte[] buffer = ArrayPool<byte>.Shared.Rent(1024 * 100);
  • LOH 제거
  • STW 80ms 이하로 감소

6. GC 로그 분석 체크리스트

✔ Gen 2 GC가 자주 발생하는가?
✔ STW 시간이 100ms를 넘는가?
✔ LOH 크기가 계속 증가하는가?
✔ GC 후 힙이 줄어드는가?
✔ 특정 타입이 힙을 독점하는가?


마무리

GC 로그는 어렵지 않습니다.

어디를 봐야 하는지만 알면 오히려 가장 정직한 성능 진단 도구입니다.

멀티스레드 환경에서 발생하는 대부분의 미묘한 성능 문제는

GC 로그 안에 이미 답이 적혀 있습니다.

다음 글에서는 실제 GC 로그를 한 줄씩 해석하는 방법
튜닝 전/후 비교 사례를 다뤄보겠습니다.

반응형