본문 바로가기

ASP.NET

[GC편 #5] ASP.NET 서버에서 자주 발생하는 GC 장애 패턴 TOP 5

들어가며

지금까지 시리즈에서는

  • GC와 멀티스레딩의 관계
  • GC 로그 분석 방법
  • GC 로그 한 줄 해석
  • GC 튜닝 전/후 비교

까지 살펴봤습니다. 이번 편에서는 한 단계 더 실무로 들어가,

ASP.NET 서버에서 실제로 가장 자주 발생하는 GC 장애 패턴

을 정리합니다.

이 글의 목표는 단순합니다.

  • 장애를 "겪고 나서" 분석하는 것이 아니라
  • 미리 로그만 보고 위험을 감지할 수 있게 만드는 것

입니다.


패턴 1. 요청당 대형 객체 생성 (LOH 폭발)

증상

  • 일정 시간마다 서버 멈춤
  • 응답 지연이 계단식으로 증가

GC 로그 특징

GC(2) Pause 700ms
LOH Size: 1.8GB

원인 코드

public IActionResult Download()
{
    return File(new byte[500_000], "application/octet-stream");
}

해결 방법

  • ArrayPool 사용
  • 스트리밍 응답
var buffer = ArrayPool<byte>.Shared.Rent(500_000);

패턴 2. 캐시 객체 누적으로 인한 Gen2 압박

증상

  • 메모리 사용량이 천천히 계속 증가
  • 재시작 전까지 회복되지 않음

GC 로그 특징

GC(2) Pause 400ms
Heap Size: 2800MB -> 2700MB

원인 코드

static Dictionary<string, object> Cache = new();

해결 방법

  • 만료 정책 도입
  • WeakReference 사용 고려

패턴 3. 비동기 코드에서의 암묵적 할당 폭증

증상

  • CPU 여유 있음
  • TPS가 기대보다 낮음

GC 로그 특징

GC(0) Pause 5ms (빈번)

원인 코드

await httpClient.GetAsync(url);
  • 클로저
  • 상태 머신 객체 생성

해결 방법

  • ValueTask 사용
  • 불필요한 async 제거

패턴 4. ThreadPool Starvation + GC 결합

증상

  • 요청 대기열 급증
  • GC Pause가 짧아도 장애 발생

GC 로그 특징

GC Pause 50ms
ThreadPool Queue Length: 증가

원인 코드

Task.Run(() => HeavyWork());

해결 방법

  • 비동기 I/O 전환
  • Task.Run 남용 금지

패턴 5. Background GC에 대한 오해

증상

  • "Background GC니까 안전"이라고 판단
  • 실시간 서비스에서 간헐적 끊김

GC 로그 특징

Background GC finished
Final Pause: 120ms

핵심 포인트

  • Background GC도 마지막엔 STW 존재
  • 짧지만 트래픽 피크와 겹치면 치명적

장애 패턴 빠른 진단 표

증상가장 먼저 볼 것

주기적 멈춤 Gen2 Pause
메모리 증가 Heap 감소폭
TPS 저하 Gen0 빈도
간헐적 끊김 Background Final Pause

마무리

GC 장애는 대부분 패턴화되어 있습니다.

같은 문제는 다른 서버에서도 같은 형태로 나타납니다.

이 다섯 가지 패턴만 익혀도,

  • 장애 원인 파악 속도가 빨라지고
  • 로그를 보는 시선이 완전히 달라지며
  • 사전 대응이 가능해집니다.
반응형