Bitmap.Save, Huge Memory Leak

2019-02-20 12:02发布

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);
                    }
                }
            }
        }
    }

5条回答
Bombasti
2楼-- · 2019-02-20 12:17

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.

查看更多
啃猪蹄的小仙女
3楼-- · 2019-02-20 12:20

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.

查看更多
不美不萌又怎样
4楼-- · 2019-02-20 12:21

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....

查看更多
做个烂人
5楼-- · 2019-02-20 12:22

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.

查看更多
走好不送
6楼-- · 2019-02-20 12:34

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

查看更多
登录 后发表回答