1

I have been using the DotSpatial library recently and noticed that my program was leaking memory quite severely. I've been using the VS memory diagnostic tools and managed to narrow the problem down the following (simplified) block of code:

using (var inMemoryStream = new MemoryStream())
using (var _writer = new BinaryWriter(inMemoryStream))
{
    WriteHeader(_writer);
   _writer.Close();
}

The BinaryWriter object maintains a reference to the inMemoryStream object, assigned to it's OutStream property. It seems that no matter what the disposal method (using / close / dispose) this memory stream never releases the memory it allocates for it's buffer.

I have managed to get around this by creating a class that inherits from BinaryWriter, overriding Close() and adding "OutStream = null" to the method, but this seems clunky.

Am I missing something? Is BinaryWriter not supposed to fully dispose of the stream that gets passed to it? It seems as if BinaryWriter.Close() does try to do something to this effect as viewing some of the properties of OutStream with intellisense shows an ObjectDisposed exception. I'm using .NET 4.5.2 if that makes a difference.

Thanks for the enlightenment in advance

  • 1
    `MemoryStream` is just a native byte array in its implementation. When both go out of scope at the end of the `using` block, they will both no longer have any references and will get cleaned up by the garbage collector. Are you sure the memory leak is not coming from `WriteHeader(_writer)`? – Ricky L. Apr 24 '18 at 14:55
  • Think about your assumption. Why would the writer dispose of a stream that was passed on to it as a reference? What if some other object that the writer does not know about is also holding a reference to the stream? – JuanR Apr 24 '18 at 15:00

2 Answers2

2

You seem to think that Dispose means "clean up the references". This can be the case, sometimes, but the actual purpose of Dispose is to release unmanaged resources. While there are many abuses of Dispose for other purposes (thanks to the handy using syntax etc.), you shouldn't expect anything more than that.

Closing a file is releasing unmanaged resources. Closing a database connection is releasing unmanaged resources. Getting rid of a reference to another managed object? Why would you ever expect that to happen as part of a Dispose? You call Dispose before you throw away the reference - you cannot ever reuse it afterward anyway, you cannot use a disposed object in any way.

Granted, there are cases where this can lead to a memory leak. But in that case, the memory leak has always been there - Dispose could only ever make it smaller. Now that's worthwhile on its own, certainly - but the solution is still to just fix the problem and release the references, not try to find ways to make the amount of leaking resources smaller.

Luaan
  • 62,244
  • 7
  • 97
  • 116
2

Dispose doesn't release managed memory. That is the job of the Garbage Collector. So while a reference to the MemoryStream is still accessible from the scope of the program (i.e. from all static variables or current locals) the memory won't be released.

Even once the MemoryStream is no longer referenced it's up to the GC exactly when the memory is released. It is not necessarily immediate or even shortly after. It depends on other factors such as overall memory pressure.

James Gaunt
  • 14,631
  • 2
  • 39
  • 57
  • 1
    I think it's worth mentioning that one **can** force immediate garbage collection with `System.GC.Collect`. https://msdn.microsoft.com/en-us/library/system.gc.collect(v=vs.110).aspx – JuanR Apr 24 '18 at 14:58
  • 1
    @JuanR good point. I'd also add though that there are probably very few scenarios where that is necessary or even beneficial and you're almost always better off letting the GC do its job. Unless perhaps you're writing something like a game where you can't afford for the GC to run at inappropriate times. – James Gaunt Apr 24 '18 at 15:02
  • Agreed 100%!... you need to fully understand what you are doing if you are to force collection in this manner. – JuanR Apr 24 '18 at 15:07