.NET Core  

Memory Leak Troubleshooting in .NET Core

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