Dave
Dave

Reputation: 1935

Finding a memory leak

I have an issue with the following code. I create a memory stream in the GetDB function and the return value is used in a using block. For some unknown reason if I dump my objects I see that the MemoryStream is still around at the end of the Main method. This cause me a massive leak. Any idea how I can clean this buffer ?

I have actually checked that the Dispose method has been called on the MemoryStream but the object seems to stay around, I have used the diagnostic tools of Visual Studio 2017 for this task.

class Program
{
    static void Main(string[] args)
    {
        List<CsvProduct> products;
        using (var s = GetDb())
        {
            products = Utf8Json.JsonSerializer.Deserialize<List<CsvProduct>>(s).ToList();
        }
    }

    public static Stream GetDb()
    {
        var filepath = Path.Combine("c:/users/tom/Downloads", "productdb.zip");
        using (var archive = ZipFile.OpenRead(filepath))
        {
            var data = archive.Entries.Single(e => e.FullName == "productdb.json");
            using (var s = data.Open())
            {
                var ms = new MemoryStream();
                s.CopyTo(ms);
                ms.Seek(0, SeekOrigin.Begin);
                return (Stream)ms;
            }
        }
    }
}

Upvotes: 1

Views: 220

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1064114

For some unknown reason if I dump my objects I see that the MemoryStream is still around at the end of the Main method.

That isn't particuarly abnormal; GC happens separately.

This cause me a massive leak.

That isn't a leak, it is just memory usage.

Any idea how I can clean this buffer ?

I would probably just not use a MemoryStream, instead returning something that wraps the live uncompressing stream (from s = data.Open()). The problem here, though, is that you can't just return s - as archive would still be disposed upon leaving the method. So if I needed to solve this, I would create a custom Stream that wraps an inner stream and which disposes a second object when disposed, i.e.

class MyStream : Stream {
    private readonly Stream _source;
    private readonly IDisposable _parent;
    public MyStream(Stream, IDisposable) {...assign...}

    // not shown: Implement all Stream methods via `_source` proxy

    public override void Dispose()
    {
        _source.Dispose();
        _parent.Dispose();
    }
}

then have:

public static Stream GetDb()
{
    var filepath = Path.Combine("c:/users/tom/Downloads", "productdb.zip");
    var archive = ZipFile.OpenRead(filepath);
    var data = archive.Entries.Single(e => e.FullName == "productdb.json");
    var s = data.Open();
    return new MyStream(s, archive);
}

(could be improved slightly to make sure that archive is disposed if an exception happens before we return with success)

Upvotes: 3

Related Questions