Garbage Collection Performance Monitor for .NET
public class GCPerformanceMonitor
{
private readonly ILogger<GCPerformanceMonitor> _logger;
private readonly Timer _monitoringTimer;
private readonly Dictionary<string, double> _metrics = new();
public GCPerformanceMonitor(ILogger<GCPerformanceMonitor> logger)
{
_logger = logger;
_monitoringTimer = new Timer(CollectMetrics, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
}
private void CollectMetrics(object state)
{
var gcInfo = GC.GetTotalMemory(false);
var gen0 = GC.CollectionCount(0);
var gen1 = GC.CollectionCount(1);
var gen2 = GC.CollectionCount(2);
var previousGen0 = _metrics.GetValueOrDefault("gen0_collections", 0);
var previousGen1 = _metrics.GetValueOrDefault("gen1_collections", 0);
var previousGen2 = _metrics.GetValueOrDefault("gen2_collections", 0);
var gen0Rate = gen0 - previousGen0;
var gen1Rate = gen1 - previousGen1;
var gen2Rate = gen2 - previousGen2;
_metrics["gen0_collections"] = gen0;
_metrics["gen1_collections"] = gen1;
_metrics["gen2_collections"] = gen2;
_metrics["total_memory"] = gcInfo;
_metrics["gen0_rate"] = gen0Rate;
_metrics["gen1_rate"] = gen1Rate;
_metrics["gen2_rate"] = gen2Rate;
// Alert on excessive Gen1/Gen2 collections
if (gen1Rate > 10 || gen2Rate > 2)
{
_logger.LogWarning("High GC activity detected. Gen1: {Gen1Rate}/min, Gen2: {Gen2Rate}/min, Memory: {Memory:N0} bytes",
gen1Rate, gen2Rate, gcInfo);
}
// Log metrics for external monitoring systems
_logger.LogInformation("GC Metrics: Gen0={Gen0Rate} Gen1={Gen1Rate} Gen2={Gen2Rate} Memory={Memory:N0}",
gen0Rate, gen1Rate, gen2Rate, gcInfo);
}
public void Dispose()
{
_monitoringTimer?.Dispose();
}
}