Search code examples
c#.net-corememory-leaks

How to leak memory in C# and .Net 7?


Im trying to study "the art of reading memory dumps" and get bit better in finding memory leaks... in order to do that I thought that I would just write a code that leaks memory in the various fashion... problem is, im having a bit of hard time doing that...

so I wrote this simple api

public class LeakMemoryController : ControllerBase
{

    public const int MEGABYTE = 1048576;
    /// <summary>
    /// 
    /// </summary>
    /// <param name="managedMemory"></param>
    /// <param name="BigBlock"></param>
    /// <param name="amountMB"></param>
    /// <returns></returns>
    [HttpGet("/leak")]
    public async Task<MemoryReport> Leak(string managedOrUnamanagedMemory, string BigOrSmallBlock, int amountMB)
    {
        var rdn = Random.Shared;
        int buffSize;
        if (BigOrSmallBlock.ToLower() == "big")
        {
            buffSize = MEGABYTE / 2;
        }
        else
        {
            //10kb
            buffSize = 10240;
        }
        var iterations = (amountMB * MEGABYTE / buffSize) + 1;

        if (managedOrUnamanagedMemory.ToLower() == "unmanaged")
        {
            for (int c = 0; c < iterations; c++)
            {
                byte[] buff = new byte[buffSize];
                for (int i = 0; i < buff.Length; i++)
                {
                    buff[i] = (byte)rdn.Next(0, 255);
                }
                unsafe
                {
                    //Try number 1
                    var a = new IntPtr(&buff);                    
                }
                //try number 2
                Marshal.AllocHGlobal(buffSize);
            }
        }
        else
        {
            for (int c = 0; c < iterations; c++)
            {
                //todo: work later
            }
        }

        return await ReportMemory();
    }

    [HttpGet("/reportMemory")]
    public async Task<MemoryReport> ReportMemory()
    {
        var process = Process.GetCurrentProcess();
        process.Refresh();
        MemoryReport report = new MemoryReport();
        report.ProcessId = process.Id;
        report.NonpagedSystemMemorySize64 = process.NonpagedSystemMemorySize64;
        report.PagedSystemMemorySize64 = process.PagedSystemMemorySize64;
        report.PagedMemorySize64 = process.PagedMemorySize64;
        report.PrivateMemorySize64 = process.PrivateMemorySize64;
        report.VirtualMemorySize64 = process.VirtualMemorySize64 / MEGABYTE;
        report.WorkingSet64 = process.WorkingSet64 / MEGABYTE;

        process.Dispose();

        return report;
    }
}

public class MemoryReport
{
    public long NonpagedSystemMemorySize64 { get; set; }
    public long PagedSystemMemorySize64 { get; internal set; }
    public long PagedMemorySize64 { get; internal set; }
    public long PrivateMemorySize64 { get; internal set; }
    public long VirtualMemorySize64 { get; internal set; }
    public long WorkingSet64 { get; internal set; }
    public int ProcessId { get; internal set; }
}

but its not leaking... no matter how many times I call "Leak", the report is always around 60-75mb...

like, what am I doing wrong?

Edit 1: Some evolution
the code below crashes the debugger:

if (managedOrUnamanagedMemory.ToLower() == "unmanaged")
{
    for (int c = 0; c < iterations; c++)
    {
        IntPtr ptr = Marshal.AllocHGlobal(buffSize);
        unsafe
        {
            var span = new Span<byte>(&ptr, buffSize);
            for (int i = 0; i < span.Length; i++)
            {
                span[i] = (byte)rdn.Next(0, 255);
            }
        }
    }
}        

Solution

  • Well, turns out that the code above does leak memory, the "problem" is that the memory doesn't stay resident (it gets paged) so the dotnet counters does not report it properly... if you check ActivityMonitor you will see the total memory process growing...