Skip to main content
 首页 » 编程设计

c#之Bitmap.Save,巨大的内存泄漏

2024年01月21日29zhengyun_ustc

我有一个应用程序,我在其中获取位图并使用 GZipStream 压缩它并通过套接字发送它,所有这些都在内存中。我已经追踪到以下行的肮脏的 scumbag 内存泄漏:

frame.Save(inStream, jpegCodec, parameters); 

浏览良好的 ol' 信息高速公路,我发现了很多关于 Image 类在各种编解码器的保存方法中泄漏内存的主题。问题是我真的找不到任何修复程序。所以我的问题如下:

  1. 是什么原因造成的
  2. 我该如何解决这个问题

这是我的 FrameStream 类中泄漏所在的完整 Write() 方法。

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

请您参考如下方法:

我建议在 CLR Profiler 下运行您的代码以找到泄漏源。如果它是任何类型的托管对象(即使是非托管资源),只要错误不是由于托管类型泄漏非托管句柄造成的,您就可以看到泄漏的位置。如果它在框架代码中,您可能可以使用反射和 P/Invoke 解决它。

例如,Icon 类型在某些情况下会泄漏 Win32 HICON。解决方法是使用 Icon 公开的句柄,通过 PInvoking DeleteObject 函数手动处理 HICON。