Created
November 26, 2025 06:52
-
-
Save devops-school/33e129696cb93456f6ee66409adefc9e to your computer and use it in GitHub Desktop.
DOTNET: Understanding Memory leaks and Debugging using dotMemory
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.Diagnostics; | |
| using System.Text; | |
| var builder = WebApplication.CreateBuilder(args); | |
| var app = builder.Build(); | |
| // =============================================== | |
| // INTENTIONAL MEMORY LEAK DEMO | |
| // =============================================== | |
| // | |
| // We simulate a classic .NET memory leak using: | |
| // - A static List<byte[]> that never gets cleared | |
| // - Each call to /leak allocates and stores large arrays | |
| // | |
| // dotMemory will show: | |
| // - Growing heap | |
| // - Many byte[] instances | |
| // - Retention via a static field | |
| // | |
| // Endpoints: | |
| // GET /leak -> allocate and store memory (leaks) | |
| // GET /noleak -> allocate but don't store (no leak) | |
| // GET /stats -> show current totals | |
| // GET /gc -> force GC to see if memory is freed | |
| // | |
| // =============================================== | |
| app.MapGet("/", () => | |
| "MemoryLeakDemo running. Use /leak, /noleak, /stats, /gc endpoints."); | |
| app.MapGet("/leak", () => | |
| { | |
| // Allocate 5 MB per call (adjust if you want more/less pressure) | |
| const int sizeInMb = 5; | |
| int bytes = sizeInMb * 1024 * 1024; | |
| var buffer = new byte[bytes]; | |
| // Touch the buffer so it really gets allocated | |
| buffer[0] = 1; | |
| buffer[bytes - 1] = 2; | |
| LeakStore.Buffers.Add(buffer); | |
| LeakStore.TotalAllocatedBytes += bytes; | |
| LeakStore.LeakCount++; | |
| return Results.Text( | |
| $"Allocated & STORED {sizeInMb} MB. " + | |
| $"Total stored: {LeakStore.Buffers.Count} buffers, ~{LeakStore.TotalAllocatedBytes / (1024 * 1024)} MB."); | |
| }); | |
| app.MapGet("/noleak", () => | |
| { | |
| // Same allocation size, but NOT stored anywhere. | |
| const int sizeInMb = 5; | |
| int bytes = sizeInMb * 1024 * 1024; | |
| var buffer = new byte[bytes]; | |
| buffer[0] = 1; | |
| buffer[bytes - 1] = 2; | |
| // Not stored → eligible for GC | |
| return Results.Text($"Allocated {sizeInMb} MB (NO LEAK) - buffer not stored."); | |
| }); | |
| app.MapGet("/stats", () => | |
| { | |
| var sb = new StringBuilder(); | |
| sb.AppendLine("=== MemoryLeakDemo Stats ==="); | |
| sb.AppendLine($"Stored buffers : {LeakStore.Buffers.Count}"); | |
| sb.AppendLine($"Total stored MB : {LeakStore.TotalAllocatedBytes / (1024 * 1024)}"); | |
| sb.AppendLine($"Leak calls count : {LeakStore.LeakCount}"); | |
| sb.AppendLine(); | |
| sb.AppendLine("Use /leak repeatedly to grow memory."); | |
| sb.AppendLine("Use /noleak to generate non-leaking allocations."); | |
| sb.AppendLine("Use /gc to force a GC and see what survives."); | |
| return Results.Text(sb.ToString()); | |
| }); | |
| app.MapGet("/gc", () => | |
| { | |
| long before = GC.GetTotalMemory(forceFullCollection: false); | |
| GC.Collect(); | |
| GC.WaitForPendingFinalizers(); | |
| GC.Collect(); | |
| long after = GC.GetTotalMemory(forceFullCollection: true); | |
| return Results.Text( | |
| $"Forced GC. Before: {before / (1024 * 1024)} MB, After: {after / (1024 * 1024)} MB. " + | |
| "Because of the static list, most leaked data will remain."); | |
| }); | |
| app.Run(); | |
| // =============================================== | |
| // LeakStore MUST come AFTER all top-level code | |
| // =============================================== | |
| static class LeakStore | |
| { | |
| // This is the "leak": static list that holds onto large arrays forever. | |
| public static readonly List<byte[]> Buffers = new(); | |
| // Metrics for convenience | |
| public static long TotalAllocatedBytes; | |
| public static long LeakCount; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment