MemoryLeakDetector 1.0.0
dotnet add package MemoryLeakDetector --version 1.0.0
NuGet\Install-Package MemoryLeakDetector -Version 1.0.0
<PackageReference Include="MemoryLeakDetector" Version="1.0.0" />
<PackageVersion Include="MemoryLeakDetector" Version="1.0.0" />
<PackageReference Include="MemoryLeakDetector" />
paket add MemoryLeakDetector --version 1.0.0
#r "nuget: MemoryLeakDetector, 1.0.0"
#:package MemoryLeakDetector@1.0.0
#addin nuget:?package=MemoryLeakDetector&version=1.0.0
#tool nuget:?package=MemoryLeakDetector&version=1.0.0
MemoryLeakDetector
🔍 A powerful .NET 8 library for detecting memory leaks through static code analysis and runtime monitoring.
Features
Static Analysis
Analyzes your C# code to find potential memory leak patterns:
- MLD001 - Missing IDisposable implementation for classes holding disposable resources
- MLD002 - Event handler subscriptions without unsubscriptions
- MLD003 - Static collections that may grow unbounded
- MLD004 - Async void methods and fire-and-forget patterns
- MLD005 - Closure captures that may cause memory leaks
- MLD006 - Timer instances not properly disposed
Runtime Monitoring
Monitor memory usage at runtime:
- Memory snapshots and trend analysis
- GC generation monitoring
- Large Object Heap tracking
- Memory growth detection
Installation
Option 1: Roslyn Analyzer (Recommended for Development)
Get real-time warnings directly in your IDE (VS Code, Visual Studio, Rider):
dotnet add package MemoryLeakDetector.Analyzers
This shows yellow/orange squiggles (warnings, not errors) as you type:
public class MyService
{
private HttpClient _client = new HttpClient(); // ⚠️ MLD001: Class should implement IDisposable
public async void ProcessAsync() // ⚠️ MLD004: Async void method detected
{
// ...
}
}
Option 2: CLI Tool (For CI/CD)
dotnet tool install -g MemoryLeakDetector.Cli
mld analyze ./MyProject
Option 3: Library (For Custom Integration)
dotnet add package MemoryLeakDetector
var report = MemoryLeakAnalyzer.AnalyzeProject(@"C:\MyProject");
Quick Start
Simple API Usage
using MemoryLeakDetector;
// Analyze a project directory
var report = MemoryLeakAnalyzer.AnalyzeProject(@"C:\MyProject");
Console.WriteLine(report);
// Check if project has critical issues (useful for CI/CD)
if (MemoryLeakAnalyzer.HasCriticalIssues(@"C:\MyProject"))
{
Console.WriteLine("Found critical memory leak issues!");
Environment.Exit(1);
}
// Analyze with options
var report = MemoryLeakAnalyzer.AnalyzeProject(@"C:\MyProject", new AnalysisOptions
{
OutputFormat = ReportFormat.Markdown,
SaveReportTo = "memory-report.md"
});
// Analyze a single file
foreach (var issue in MemoryLeakAnalyzer.AnalyzeFile(@"C:\MyProject\MyClass.cs"))
{
Console.WriteLine($"[{issue.Severity}] {issue.Title}");
}
// Analyze code directly
var code = @"
public class MyService
{
private HttpClient _client = new HttpClient();
}";
foreach (var issue in MemoryLeakAnalyzer.AnalyzeCode(code))
{
Console.WriteLine($"{issue.Id}: {issue.Description}");
}
CLI Usage
# Analyze a project (code + NuGet packages)
mld analyze ./MyProject
# Scan only NuGet packages
mld nuget ./MyProject
# Scan NuGet with verbose output
mld nuget ./MyProject -v
# Analyze with JSON output
mld analyze ./MyProject -f Json -o report.json
# Quick check (returns exit code for CI/CD)
mld check ./MyProject
# Show current memory info
mld memory
# Force GC
mld gc
NuGet Scanning
// Scan only NuGet packages
foreach (var issue in MemoryLeakAnalyzer.AnalyzeNuGetPackages(@"C:\MyProject"))
{
Console.WriteLine($"[{issue.TypeName}] {issue.Title}");
Console.WriteLine($" {issue.Recommendation}");
}
Runtime Monitoring
using MemoryLeakDetector;
using MemoryLeakDetector.Runtime;
// Create a monitor with auto-snapshots
using var monitor = MemoryLeakAnalyzer.CreateMonitor(autoSnapshot: true);
// Or manually take snapshots
var snapshot = monitor.TakeSnapshot();
Console.WriteLine($"Memory: {snapshot.TotalManagedMemory:N0} bytes");
// Analyze trends (after collecting several snapshots)
foreach (var issue in monitor.AnalyzeTrends())
{
Console.WriteLine($"[{issue.Severity}] {issue.Title}");
}
// Force GC and see how much was freed
var freed = MemoryLeakAnalyzer.ForceGarbageCollection();
Console.WriteLine($"Freed: {freed:N0} bytes");
Configuring Analyzer Severity
All rules are warnings by default (yellow squiggles). You can customize severity in .editorconfig:
# .editorconfig
# Disable a specific rule
dotnet_diagnostic.MLD001.severity = none
# Make a rule an error (will fail build)
dotnet_diagnostic.MLD004.severity = error
# Make a rule a suggestion (light bulb only)
dotnet_diagnostic.MLD002.severity = suggestion
Or in your .csproj:
<PropertyGroup>
<WarningsAsErrors>MLD001;MLD004;MLD006</WarningsAsErrors>
<NoWarn>MLD003</NoWarn>
</PropertyGroup>
What It Detects
🟡 Memory Leak Analyzers (Yellow/Orange - Warning)
| ID | Issue |
|---|---|
| MLD001 | Missing IDisposable implementation |
| MLD002 | Event handler subscription without unsubscription |
| MLD003 | Static collection may grow unbounded |
| MLD004 | Async void method |
| MLD006 | Timer not disposed |
| MLD007 | HttpClient created per request / in using |
| MLD008 | DbContext stored in long-lived field |
🔵 Performance Analyzers (Blue - Info/Suggestion)
| ID | Issue |
|---|---|
| PERF001 | String concatenation in loop |
| PERF002 | LINQ method in loop / Multiple enumeration |
| PERF003 | Boxing allocation in hot path |
| PERF004 | Regex not compiled / Static Regex.IsMatch |
| PERF005 | Async method without await / Unnecessary async |
| PERF006 | List created without capacity |
| PERF007 | ContainsKey + indexer (use TryGetValue) |
| PERF008 | Params array allocation in loop |
| PERF011 | String.ToLower() for comparison |
| PERF012 | Closure allocation in loop |
| PERF013 | JsonSerializerOptions created per call |
💡 Severity Levels
| Color | Severity | Meaning |
|---|---|---|
| 🟡 Yellow/Orange | Warning | Likely bug or memory leak |
| 🔵 Blue | Info | Performance suggestion |
| 💡 Light bulb | Suggestion | Consider improving |
Quick Start
// ❌ BAD: Class holds disposable resource but doesn't implement IDisposable
public class MyService
{
private HttpClient _client = new HttpClient();
}
// ✅ GOOD: Properly implements IDisposable
public class MyService : IDisposable
{
private HttpClient _client = new HttpClient();
private bool _disposed;
public void Dispose()
{
if (!_disposed)
{
_client?.Dispose();
_disposed = true;
}
}
}
MLD002: Event Handler Leaks
// ❌ BAD: Subscribes but never unsubscribes
public class MyHandler
{
public MyHandler(EventSource source)
{
source.DataReceived += OnDataReceived;
}
}
// ✅ GOOD: Properly unsubscribes
public class MyHandler : IDisposable
{
private EventSource _source;
public MyHandler(EventSource source)
{
_source = source;
_source.DataReceived += OnDataReceived;
}
public void Dispose()
{
_source.DataReceived -= OnDataReceived;
}
}
MLD003: Static Collection Growth
// ❌ BAD: Static collection can grow unbounded
public static class Cache
{
private static readonly Dictionary<string, object> _cache = new();
public static void Add(string key, object value) => _cache[key] = value;
}
// ✅ GOOD: Use bounded cache or MemoryCache
public static class Cache
{
private static readonly MemoryCache _cache = new(new MemoryCacheOptions
{
SizeLimit = 1000
});
}
MLD004: Async Void
// ❌ BAD: Async void can't be awaited, exceptions are unobservable
public async void ProcessDataAsync()
{
await Task.Delay(100);
}
// ✅ GOOD: Return Task
public async Task ProcessDataAsync()
{
await Task.Delay(100);
}
MLD005: Closure Captures
// ❌ BAD: Lambda captures 'this', preventing GC
public class MyService
{
public void Subscribe(EventAggregator aggregator)
{
aggregator.Subscribe(() => this.DoWork()); // Captures 'this'
}
}
// ✅ GOOD: Capture only what's needed
public class MyService
{
public void Subscribe(EventAggregator aggregator)
{
var work = GetWorkDelegate(); // Capture minimal state
aggregator.Subscribe(() => work());
}
}
MLD006: Timer Not Disposed
// ❌ BAD: Timer keeps firing after object should be GC'd
public class Poller
{
private Timer _timer;
public Poller()
{
_timer = new Timer(_ => Poll(), null, 0, 1000);
}
}
// ✅ GOOD: Dispose the timer
public class Poller : IDisposable
{
private Timer _timer;
public Poller()
{
_timer = new Timer(_ => Poll(), null, 0, 1000);
}
public void Dispose()
{
_timer?.Dispose();
}
}
CI/CD Integration
GitHub Actions
- name: Check for memory leaks
run: |
dotnet tool install -g MemoryLeakDetector.Cli
mld check ./src
Azure DevOps
- script: |
dotnet tool install -g MemoryLeakDetector.Cli
mld check ./src
displayName: 'Memory Leak Check'
Report Formats
Console (default)
Human-readable output with colors and emojis.
JSON
Machine-readable format for integration with other tools.
Markdown
Perfect for including in PRs or documentation.
Extending
Add custom analyzers by implementing ICodeAnalyzer:
public class MyCustomAnalyzer : SyntaxAnalyzerBase
{
public override string AnalyzerId => "CUSTOM001";
public override string AnalyzerName => "My Custom Check";
public override IssueCategory Category => IssueCategory.MissingDispose;
public override IEnumerable<MemoryIssue> Analyze(SyntaxTree syntaxTree, SemanticModel? semanticModel)
{
// Your analysis logic here
yield break;
}
}
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.CodeAnalysis.CSharp (>= 4.8.0)
- Microsoft.CodeAnalysis.CSharp.Workspaces (>= 4.8.0)
- Microsoft.Diagnostics.Runtime (>= 3.1.512801)
- Microsoft.Extensions.DependencyInjection (>= 8.0.0)
- Microsoft.Extensions.Hosting (>= 8.0.0)
- Microsoft.Extensions.Logging (>= 8.0.0)
- Microsoft.Extensions.Logging.Console (>= 8.0.0)
- Newtonsoft.Json (>= 13.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 302 | 12/17/2025 |