Introduction
Memory leaks can silently degrade the performance of your .NET Core applications, leading to high memory consumption, sluggish performance, or even crashes. Fortunately, .NET provides a rich set of tools to detect, analyze, and fix memory leaks effectively. In this article, we'll walk step-by-step through identifying and resolving memory leaks using practical tools and a real-world example.
Prerequisites
Before diving into debugging memory leaks, ensure you have the following installed and set up.
- .NET SDK 6.0+
- dotnet-counters tool
- dotnet-dump tool
- Basic understanding of C# and ASP.NET Core
To install tools.
dotnet tool install --global dotnet-counters
dotnet tool install --global dotnet-dump
Creating a Sample ASP.NET Core Application
Add a Leaky Controller
In the Controllers folder, create a new file called LeakController.cs.
[Route("api/[controller]")]
[ApiController]
public class LeakController : ControllerBase
{
private static Dictionary<int, byte[]> _leakyCache = new();
[HttpGet("{kb}")]
public IActionResult LeakMemory(int kb)
{
var buffer = new byte[kb * 1024];
_leakyCache[DateTime.UtcNow.Ticks.GetHashCode()] = buffer;
return Ok(new
{
Message = $"Leaked {kb} KB. Cache size: {_leakyCache.Count}"
});
}
}
Each request allocates a byte array and stores it in a static dictionary using the current timestamp as a key. Since we never remove old items, this creates a memory leak.
Running the Application
dotnet run
The API should be available at https://localhost:7261/api/leak/{kb}. This leaks 1000 KB of memory. To simulate repeated leaking.
for i in {1..50}; do curl http://localhost:7261/api/leak/1000; done
Monitoring with dotnet-counters
Use dotnet-counters to monitor real-time performance metrics.
Get the process ID (PID) of your running app.
dotnet-counters ps
![Dotnet counters]()
Now, run the below command to get the different parameters along with the memory size.
dotnet-counters monitor --refresh-interval 1 -p <PID>
![Memory leak]()
What to Watch.
- GC Heap Size: Should grow steadily
- Gen 0/1/2 GC Count: Garbage collections increase, but the EAP never shrinks
This confirms that objects aren’t being collected – a classic memory leak.
Capturing Memory Dumps
We capture a snapshot of the memory to analyze it.
dotnet-dump collect -p <PID> -o before_leak.dmp
![Memory Dumps]()
Trigger more leaks.
for i in {1..100}; do curl http://localhost:5000/api/leak/1000; done
Then collect another snapshot.
dotnet-dump collect -p <PID> -o after_leak.dmp
Fixing the Leak
Limit the size of the dictionary.
if (_leakyCache.Count > 100)
_leakyCache.Clear();
Validating the Fix
Restart the app, monitor with dotnet-counters, and send repeated requests. You should now see memory usage stabilize.
Conclusion
Memory leaks in .NET Core are subtle but deadly. With dotnet-counters, dotnet-dump, and analysis tools like SOS and Visual Studio, you can detect and fix them effectively. This article walked you through building a leaky app, detecting issues, analyzing heap dumps, and resolving the leak. Regular diagnostics and profiling should be part of your development and deployment cycle to ensure smooth performance.
Thank You, and Stay Tuned for More!
More Articles from my Account