Reputation: 8770
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
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.
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
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
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
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
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