views:

1039

answers:

5

I have an application where I am taking a bitmap and compressing it using a GZipStream and sending it over a socket, all in memory. I have tracked down the dirty scumbag memory leak to the following line:

frame.Save(inStream, jpegCodec, parameters);

Browsing around the good ol' information superhighway I have found numerous topics about the Image class leaking memory in the save method on various codecs. Problem is there aren't really any fixes out there that I could find. So my questions are as follows:

  1. What causes this
  2. How can I fix this

Here is my full Write() method in my FrameStream class where the leak is located.

/// <summary>
    /// Writes a frame to the stream
    /// </summary>
    /// <param name="frame">The frame to write</param>
    public void Write(Bitmap frame) {
        using (EncoderParameter qualityParameter = new EncoderParameter(Encoder.Quality, 50L)) {
            using (EncoderParameters parameters = new EncoderParameters(1)) {
                parameters.Param[0] = qualityParameter;

                ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
                ImageCodecInfo jpegCodec = null;

                foreach (ImageCodecInfo codec in codecs) {
                    if (codec.MimeType == "image/jpeg") {
                        jpegCodec = codec;
                        break;
                    }
                }

                using (MemoryStream inStream = new MemoryStream()) {
                    frame.Save(inStream, jpegCodec, parameters); // HUUUGE Memory Leak
                    Byte[] buffer = new Byte[inStream.Length];
                    inStream.Read(buffer, 0, buffer.Length);

                    using (MemoryStream outStream = new MemoryStream()) {
                        using (GZipStream gzipStream = new GZipStream(outStream, CompressionMode.Compress)) {
                            gzipStream.Write(buffer, 0, buffer.Length);
                        }

                        Byte[] frameData = outStream.ToArray();
                        Byte[] packet = new Byte[15 + frameData.Length];
                        Byte[] frameLength = BitConverter.GetBytes(frameData.Length);

                        Array.Copy(frameLength, 0, packet, 0, frameLength.Length);
                        Array.Copy(frameData, 0, packet, 15, frameData.Length);

                        m_Socket.Send(packet);
                    }
                }
            }
        }
    }
A: 

I don't know much about sockets. However, I know one way to stop a memory leak with the Image class is to freeze the bitmap. Hopefully, this post might provide you with some more information. Could the MemoryStream.Dispose be failing? Thereby, creating a memory leak.

daub815
A: 

Are you calling the .Dispose() method of your Graphics object? That will cause a memory leak. EDIT: Once you have written the byte[], you are now clear to .Dispose() of the Bitmap object.

Wayne Hartman
If you comment out frame.Save(..) there is no memory leak, and all graphics objects are disposed and accounted for. From my searching around, the bitmap.save memory leaks were marked as a bug in the framework my msft. don't know if there's a fix, which is what im looking for
David Anderson
@David: You might want to add that information to the Question, saves some confusion.
Henk Holterman
A: 

You should set the bitmap to null when done working with it, after disposal.

Also, you may want to invoke the Garbage Collector after disposal of the Bitmap (even though it is an expensive operation): GC.Collect();

Bitmap holds unmanaged resources - the GC is not always "on the ball" with these. Here is an interesting link regarding the Bitmap class (from the perspective of the compact framework): http://blog.opennetcf.com/ctacke/PermaLink,guid,987041fc-2e13-4bab-930a-f79021225b74.aspx

Demi
as a side thought, perhaps one could be sneaky and use a thread to do the saving. When the thread exits, especially along with resource-holding object disposal, you should be able to reclaim the leaking memory. I could be wrong, but it might be worth a try if everything else fails.
Demi
+2  A: 

I suggest running your code under CLR Profiler to locate the source of the leak. If it's a managed object of any type (even an unmanaged resource), as long as the bug isn't due to a managed type leaking an unmanaged handle, you'll be able to see where the leak is. If it's in framework code, you can probably work around it using reflection and P/Invoke.

For example, the Icon type leaks Win32 HICONs in some circumstances. The workaround for this is to manually dispose the HICON by PInvoking the DeleteObject function, using the handle exposed by the Icon.

Kevin Gadd
+2  A: 

Okay, after trying everyones ideas and thoughts and numerous other methods. I finally tried the simple:

using (frame) {
    frame.Save(outStream, jpegCodec, parameters);
}

And well, this worked and the memory leak is fixed.. I tried forcefully invoking the garbage collector, disposing the bitmap manually, using P/Invoke DeleteObject, nothing worked, but using a using statement did. So this makes me wonder what happens underthehood on a using statement that I'm missing out on....

David Anderson
It just calls Dispose. You haven't really shown enough code to let us know how you were calling the Write method above - I suspect you weren't really disposing of the bitmap.
Jon Skeet
In the code I posted above I left out the frame.Dispose() line after the m_Socket.Send(...), but calling dispose on the frame did not make a difference in my testing, it still allocated much memory
David Anderson
Turns out after further investigation the its a memory build up of queued bitmaps as the socket in the app cannot send out packets fast enough to keep up with how many bitmaps are queued
David Anderson