Deathspike
Deathspike

Reputation: 8770

C# MemoryStream leaking memory, after disposing/close/etc?

I've been tracking massive memory leeking in my application and it seems the issue is the MemoryStream class. Whenever I use one, either with the 'using' key word or explicit close/dispose, the memory will never be collected by the garbage collector. What is wrong here?

byte[] bData = System.IO.File.ReadAllBytes( "F:\\application_exit_bw.png" );
using( System.IO.MemoryStream hMemoryStreamOutput = new System.IO.MemoryStream())
{
    for ( int i = 0; i < 10000; i++ ) hMemoryStreamOutput.Write( bData, 0, bData.Length );
}
Thread.Sleep(Timeout.Infinite);

With explicit close/dipose the behaviour stays the same. Memory is occupied and will stay that way until I close my application, or, the application filled all of the system memory. Help?

Upvotes: 4

Views: 7729

Answers (5)

Alexei Levenkov
Alexei Levenkov

Reputation: 100527

Another side of the problem is what you are using to determine "memory leak". There are many different ways to measure "free' memory and depending on it you may get tottaly different results.

  • Memory usage show in Task manager - unlikely to go down even when all memory is considered "free" by CLR GC due to the way GC uses memory.
  • GC memory performance counters (and properties) - these ones actually will show GC's view on memory. You want to use them to detect managed memory leaks.

There is one more thing with MemoryStream (and any other large 86K+) allocations - they use Large Objects Heap that will be only collected on full GC, to trigger it you may need to run GC.Collect twice. In normal flow of an application it will happen rare enough, so you may not see this memory freed before application shutdown. To diagnose - check GC collection performance counter (number of GC).

And one more: if you are trying to solve memory leak because you are getting "out of memory" exception it may be caused by address space fragmentation (normally only for 32-bit processes). If it is the case - consider creating your own memory stream that does not allocate memory in single chunk and then have to copy it when growing the stream. Or at least try to preallocate space in the stream.

Upvotes: 1

Michael Brennt
Michael Brennt

Reputation: 751

Calling GC.Collect() is not a good practise and shouldn't be used as a solution.

You can try and see if that changes anything but do not rely on intentional GC.Collect() call...

Upvotes: 0

user1865292
user1865292

Reputation: 11

I have used this method for batch processing

    static byte[] buffer;

    public static object Read(XmlDocument xmlDocument)
    {
        if (buffer == null)
        {
            buffer = new byte[1024 * 1024 * 512];
        }

        if (xmlDocument != null)
        {
            using (MemoryStream ms = new MemoryStream(buffer))
            {
                xmlDocument.Save(ms);
                ms.Flush();
                ms.Seek(0, SeekOrigin.Begin);

                object  result = ReadFromStream(ms);

                ms.Close();
                return result;
            }
        }
        else
        {
            return null;
        }
    }

Upvotes: 0

JaredPar
JaredPar

Reputation: 754635

There is nothing wrong with the MemoryStream class or the usage within your sample code. The GC in .Net doesn't clean up memory immediately after it's no longer. Instead it reclaims it when free space in the heap reaches a certain threshold or it's explicitly invoked with a GC.Collect call.

In this scenario the only way the memory would be freed is if a GC occurred immediately after the using statement and before the Thread.Sleep call. This is fairly unlikely to happen and hence if you profiled the program it would have the appearance of a memory leak when it's not actually leaking

Upvotes: 8

usr
usr

Reputation: 171188

This is a symptom of a non-deterministic GC. The GC does not make any guarantees at all about when memory will be freed. This is normal, expected and wanted behavior.

Try calling GC.Collect() to see if this fixes your problem. Also, you need to run in release mode because in debug mode the JIT extends the lifetime of local variables to the end of the method even if they are not used after some point.

Upvotes: 6

Related Questions