GC-동작-시간-확인
GC의 동작 시간을 코드에서 모니터링하려면 GC 이벤트가 발생한 시점을 기록하고, 해당 이벤트가 완료될 때까지의 시간을 측정해야 합니다. ETWEvent Tracing for Windows 이벤트를 활용하거나, 타이머를 사용하여 직접 측정하는 방법을 사용할 수 있습니다.
GC 강제 호출
GC.Collect()
를 명시적으로 호출하고 시간 측정을 수행하여 GC의 소요 시간을 측정할 수 있습니다.
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
Stopwatch sw = new Stopwatch();
// Full GC 강제 호출 및 시간 측정
sw.Start();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
sw.Stop();
Console.WriteLine($"GC 수행 시간: {sw.ElapsedMilliseconds} ms");
}
}
- 이 방법은 실제 GC 작업에 의해 트리거된 시간이 아니라 강제 호출로 측정된 시간입니다.
- 실시간 GC 모니터링에는 적합하지 않습니다.
ETW 사용
.NET 런타임에서 발생하는 GC 이벤트를 ETWEvent Tracing for Windows로 캡처하면 실시간으로 GC의 동작 시간을 측정할 수 있습니다.
GC 관련 ETW 이벤트
GCStart_Vx
- GC 시작을 나타내는 이벤트
- GC의 세대에 따라 표시다 달라짐.
- 예 : Gen0 : GCStart_V0
GCEnd_Vx
- GC 완료를 나타내는 이벤트
GCAllocationTick_Vx
- 메모리 할당 시 발생하는 이벤트로, 작은 객체가 할당될 때마다 트리거
- GC 자체보다는 메모리 사용량 추적에 더 유용
GCSuspendEEBegin
- GC가 실행되기 전 Execution Engine(EE)을 일시 중단하는 이벤트.
GCSuspendEEEnd
- GC 완료 후 Execution Engine(EE)을 다시 시작하는 이벤트.
GCFinalizersBegin
및 GCFinalizersEnd
- GC가 최종화 작업(Finalization)을 수행할 때 발생
- 특정 객체가 Finalizer 큐에 있을 경우 트리거
GCHeapStats_Vx
- GC 힙 상태를 제공하는 이벤트로, Gen0/Gen1/Gen2의 크기 및 메모리 사용량 모니터링
System.Diagnostics.Tracing 사용
.NET Core 3.0 이상에서 EventListener
를 사용하여 GC 이벤트를 구독하고 동작 시간을 측정할 수 있습니다.
using System;
using System.Diagnostics.Tracing;
using System.Threading;
class Program
{
static void Main()
{
var listener = new GCEventListener();
Console.WriteLine("GC 모니터링 시작...");
Thread.Sleep(Timeout.Infinite); // 프로그램을 유지
}
}
class GCEventListener : EventListener
{
public Stopwatch sw = new Stopwatch();
int gcElapsed;
private DateTime _gcStartTime;
protected override void OnEventSourceCreated(EventSource eventSource)
{
// GC 관련 이벤트를 구독
if (eventSource.Name == "Microsoft-Windows-DotNETRuntime")
{
//0x1: GC 이벤트.
//0x10: 기본 GC 이벤트를 활성화.
//0x20: 특정 추가 GC 이벤트를 활성화.
EnableEvents(eventSource, EventLevel.Verbose, (EventKeywords)(0x1 | 0x10 | 0x20));
}
}
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
if (eventData.EventName!.Equals("GCSuspendEEBegin"))
sw.Restart();
else if (eventData.EventName!.Equals("GCSuspendEEEnd"))
{
gcElapsed = sw.GetElapsedMicroseconds();
Console.WriteLine($"GC : {gcElapsed}");
}
}
}
- 실시간 GC 모니터링 가능.
- GC의 시작과 종료 시간을 이벤트 기반으로 측정.
.NET Diagnostic Tools 사용
.NET Core 3.0 이상에서는 DiagnosticListener
와 Activity
를 사용하여 GC 관련 데이터를 모니터링할 수 있습니다.
using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
class Program
{
static void Main()
{
var listener = new DiagnosticListener("GC_Monitor");
GC.Collect();
Console.ReadLine();
}
}
class GCEventListener : EventListener
{
private Stopwatch _stopwatch;
protected override void OnEventSourceCreated(EventSource eventSource)
{
if (eventSource.Name == "System.Runtime")
{
EnableEvents(eventSource, EventLevel.Informational, EventKeywords.None);
}
}
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
if (eventData.EventName == "GCStart")
{
_stopwatch = Stopwatch.StartNew();
Console.WriteLine("GC 시작...");
}
else if (eventData.EventName == "GCEnd")
{
_stopwatch.Stop();
Console.WriteLine($"GC 종료. 소요 시간: {_stopwatch.ElapsedMilliseconds} ms");
}
}
}
dotnet-counters 사용
설정 및 실행
- .NET CLI에서
dotnet-counters
설치:
dotnet tool install --global dotnet-counters
- 특정 프로세스의 GC 데이터를 실시간으로 모니터링:
dotnet-counters monitor --process-id <PID> System.Runtime
- 주요 카운터
time-in-gc
: GC에 소요된 총 시간 비율.gen-0-gc-count
,gen-1-gc-count
,gen-2-gc-count
: 각 세대의 GC 횟수.
비용 분석: ETW vs DiagnosticListener
두 방식 모두 비용이 발생하지만, 목적에 따라 비용 효율이 다를 수 있습니다.
ETW(EventListener)
장점
- 효율적 설계: ETW는 운영 체제 수준에서 고성능으로 설계되었으며, 이벤트 추적 비용이 낮음.
- 세부적인 데이터: GC와 관련된 이벤트(GCStart, GCEnd 등)를 상세히 제공.
- 실시간 모니터링: GC 이벤트를 즉시 확인 가능.
비용
- 이벤트 구독 비용:
EventListener
는 이벤트를 수신할 때만 비용이 발생.- GC 이벤트 자체는 적은 양의 메모리를 사용하며, 이벤트가 자주 발생하지 않으면 부담이 크지 않음.
- CPU 사용량:
- 이벤트 수신 및 처리 로직에서 CPU 사용량이 증가할 수 있음.
- 처리할 이벤트의 양이 많을 경우 CPU 사용량이 증가.
- 메모리 사용량:
- 이벤트 데이터를 저장하거나 분석할 경우 추가 메모리 소비.
적합한 사용 사례
- 실시간 모니터링이 필요한 경우.
- GC 이벤트만 필터링하여 확인하는 경우.
- 전체 애플리케이션 성능에 큰 영향을 주지 않는 시나리오.
DiagnosticListener
장점
- 구체적인 컨텍스트: 이벤트 외에도 진단 정보와 애플리케이션의 실행 컨텍스트를 함께 제공.
- 다양한 진단 이벤트: GC 외에도 HTTP, 데이터베이스 쿼리 등 애플리케이션 이벤트를 폭넓게 모니터링 가능.
비용
- 초기화 비용:
DiagnosticListener
는 이벤트 외에도 다양한 진단 데이터를 제공하므로,EventListener
에 비해 초기화 비용이 약간 더 높음.
- 이벤트 처리 비용:
DiagnosticListener
는 더 많은 범위의 데이터를 처리하므로 이벤트 처리 비용이 상대적으로 높음.- 이벤트가 많을 경우, 특히 실시간 분석이 추가된다면 성능 영향을 받을 수 있음.
- 메모리 사용량:
- 추가 데이터를 함께 처리하므로 이벤트당 메모리 소비량이
EventListener
보다 더 높음.
- 추가 데이터를 함께 처리하므로 이벤트당 메모리 소비량이
적합한 사용 사례
- 광범위한 애플리케이션 진단: GC뿐 아니라, HTTP 요청, 데이터베이스 쿼리 등의 상태를 함께 모니터링해야 할 때.
- 운영 환경 진단: ETW 수준의 데이터를 제공하지 않아도 되는 상황.
비교: ETW vs DiagnosticListener
특징 | EventListener | DiagnosticListener |
---|---|---|
설계 목적 | 운영 체제 수준 이벤트 추적 | .NET 애플리케이션 내부 진단 이벤트 |
CPU 사용량 | 낮음 | 중간 |
메모리 사용량 | 적음 | 상대적으로 많음 |
추적 가능 범위 | GC와 같은 런타임 이벤트 추적 | GC, HTTP 요청, 데이터베이스 등 |
이벤트 처리 효율성 | 매우 효율적 | 추가 데이터 처리로 인해 비용 증가 |
적합한 상황 | 단일 목적(GC 이벤트 등) | 복합 목적(애플리케이션 상태 모니터링) |