본문 바로가기

카테고리 없음

[GC편 #3] GC 로그 한 줄씩 해석하기: 로그만 봐도 원인이 보이게 만드는 법

들어가며

앞선 글에서 GC 로그를 수집하는 방법어디를 봐야 하는지를 살펴봤습니다.
이번 글에서는 한 단계 더 나아가,

GC 로그 한 줄 한 줄이 무엇을 의미하는지

를 실제 예제를 통해 해석해봅니다.

이 글을 끝까지 읽고 나면,

  • GC 로그를 봤을 때 당황하지 않고
  • 성능 문제의 원인을 머릿속에 그릴 수 있으며
  • 코드 레벨에서 무엇을 고쳐야 할지 감이 잡히게 됩니다.

 

가비지 수집 및 성능 - .NET

가비지 수집 및 메모리 사용과 관련된 문제에 대해 알아봅니다. 가비지 수집이 애플리케이션에 미치는 영향을 최소화하는 방법을 알아봅니다.

learn.microsoft.com

1. 가장 흔히 보게 되는 GC 로그 형태

먼저 대표적인 GC 로그 한 줄을 살펴보겠습니다.

[GC (2) Pause] Reason: Allocation Failure
Heap Size: 2048MB -> 1980MB
Pause Time: 350ms

이 짧은 로그 안에 매우 중요한 정보가 들어 있습니다.


2. GC (2)의 의미

GC (2)

이는 Gen 2 GC가 발생했음을 의미합니다.

왜 중요한가?

  • Gen 2 GC는 가장 무거운 GC
  • Stop-The-World 시간이 길어짐
  • 서버 전체가 멈춘 것처럼 보일 수 있음

👉 운영 환경에서 잦은 Gen 2 GC는 거의 항상 문제 신호입니다.


3. Pause 이유 해석하기

Reason: Allocation Failure

이는 새로운 객체를 할당하려 했지만,

  • 충분한 연속 메모리를 확보하지 못해
  • GC를 강제로 실행했다는 의미입니다.

자주 보이는 Reason 유형

Reason의미

Allocation Failure 힙이 꽉 참
Induced GC.Collect() 호출
Low Memory OS 메모리 압박

👉 Allocation Failure가 반복되면 객체 생성 패턴부터 의심해야 합니다.


4. Heap Size 변화 읽기

Heap Size: 2048MB -> 1980MB

이 부분은 GC 전후 힙 크기 변화를 의미합니다.

해석 포인트

  • 줄어든 폭이 작다 → 대부분 객체가 살아남음
  • 거의 줄지 않는다 → 메모리 누수 의심
3000MB -> 2900MB  ← 위험 신호

👉 GC가 일을 했는데 메모리가 안 줄면, 구조적 문제가 있습니다.


5. Pause Time이 말해주는 것

Pause Time: 350ms

350ms는 서버 애플리케이션 기준으로 이미 사용자 체감 수준입니다.

일반적인 기준

Pause Time영향

< 50ms 안전
50~100ms 주의
100~300ms 체감
> 300ms 장애 수준

멀티스레드 서버에서는 이 시간 동안 모든 요청이 정지됩니다.


6. 연속된 로그로 패턴 읽기

GC 로그는 한 줄만 보면 안 됩니다.

GC(0) Pause 5ms
GC(0) Pause 4ms
GC(1) Pause 12ms
GC(2) Pause 420ms

이 패턴이 의미하는 것

  • Gen 0/1은 빠르게 정리됨
  • Gen 2에서 한 번에 크게 멈춤

➡ 장수 객체가 많고, 누적되다가 터지는 구조


7. LOH 관련 로그 해석

LOH Size: 1.6GB
LOH Fragmentation: 45%

해석

  • LOH가 크다
  • 단편화가 심하다
  • 할당 실패 가능성 높음

👉 이 로그가 보이면 거의 항상 다음 코드가 있습니다.

new byte[100_000];

8. Background GC 로그 이해하기

Background GC started
Background GC finished
  • Gen 2 GC가 백그라운드에서 수행됨
  • 하지만 마지막엔 짧은 STW 존재
Final Pause: 80ms

👉 Background GC가 있어도 완전 무정지는 아니다라는 점이 중요합니다.


9. 로그를 코드로 연결하는 사고 방식

GC 로그를 보면 이렇게 생각해야 합니다.

1️⃣ 어떤 세대의 GC인가?
2️⃣ 왜 이 시점에 발생했는가?
3️⃣ 힙은 줄었는가?
4️⃣ 멈춘 시간은 얼마인가?
5️⃣ 어떤 코드가 이 패턴을 만들었을까?

이 질문에 답이 연결되면, 수정 방향도 보입니다.


10. 실전 예제: 로그 → 코드 개선

로그

GC(2) Pause 600ms
LOH Size: 2.1GB

추론

  • 대형 객체 반복 생성
  • 요청 단위 버퍼 할당

개선 코드

byte[] buffer = ArrayPool<byte>.Shared.Rent(200_000);
try
{
    Process(buffer);
}
finally
{
    ArrayPool<byte>.Shared.Return(buffer);
}

결과

GC(2) Pause 70ms
LOH Size: 300MB

마무리

GC 로그는 숫자와 텍스트의 나열이 아닙니다.

애플리케이션의 메모리 사용 습관을 그대로 보여주는 기록입니다.

로그를 읽을 수 있게 되면,

  • 성능 문제를 감으로 추측하지 않게 되고
  • 장애 원인을 재현 없이도 설명할 수 있으며
  • 코드 리뷰의 깊이가 달라집니다.

다음 글에서는 GC 튜닝 전/후 로그 비교 사례를 통해
"어떻게 바뀌었는지"를 시각적으로 정리해보겠습니다.

반응형