Bitmap.Save, огромная утечка памяти - PullRequest
1 голос
/ 16 мая 2009

У меня есть приложение, в котором я беру растровое изображение и сжимаю его с помощью GZipStream и отправляю по сокету, все в памяти. Я проследил утечку памяти грязного подонка до следующей строки:

frame.Save(inStream, jpegCodec, parameters);

Просматривая хорошую информационную супермагистраль, я нашел множество тем о утечке памяти в классе Image в методе сохранения различных кодеков. Проблема в том, что там нет никаких исправлений, которые я мог бы найти. Поэтому мои вопросы таковы:

  1. Что вызывает это
  2. Как я могу это исправить

Вот мой полный метод Write () в моем классе FrameStream, в котором находится утечка.

/// <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 ]

4 голосов
/ 16 мая 2009

Я предлагаю запустить ваш код под CLR Profiler, чтобы определить источник утечки. Если это управляемый объект любого типа (даже неуправляемый ресурс), если ошибка не связана с управляемым типом, у которого происходит утечка неуправляемого дескриптора, вы сможете увидеть, где находится утечка. Если он находится в коде фреймворка, вы, вероятно, можете обойти его, используя отражение и P / Invoke.

Например, в некоторых случаях тип Icon пропускает Win32 HICON s. Обходной путь для этого состоит в том, чтобы вручную утилизировать HICON, вызывая функцию DeleteObject, используя дескриптор, открываемый Icon.

3 голосов
/ 16 мая 2009

Хорошо, попробовав все идеи и мысли и многочисленные другие методы. Я наконец попробовал простое:

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

И хорошо, это сработало, и утечка памяти устранена. Я пытался принудительно вызвать сборщик мусора, уничтожить растровое изображение вручную, используя P / Invoke DeleteObject, ничего не сработало, но с помощью оператора using это сделал. Так что это заставляет меня задуматься о том, что происходит под недоверием в утверждении об использовании, в котором я упускаю ....

2 голосов
/ 16 мая 2009

Вы должны установить растровое изображение на ноль, когда закончите работу с ним, после утилизации.

Кроме того, вы можете вызвать сборщик мусора после удаления растрового изображения (даже если это дорогостоящая операция): GC.Collect();

Битовая карта содержит неуправляемые ресурсы - GC не всегда "на высоте" с ними. Вот интересная ссылка, касающаяся класса Bitmap (с точки зрения компактного фреймворка): http://blog.opennetcf.com/ctacke/PermaLink,guid,987041fc-2e13-4bab-930a-f79021225b74.aspx

0 голосов
/ 16 мая 2009

Вы вызываете метод .Dispose () вашего объекта Graphics? Это вызовет утечку памяти. РЕДАКТИРОВАТЬ: После того, как вы записали байт [], теперь вы очищены .Dispose () объекта Bitmap.

0 голосов
/ 16 мая 2009

Я не знаю много о розетках. Однако я знаю, что один из способов предотвратить утечку памяти с помощью класса Image - заморозить растровое изображение. Надеюсь, этот пост может предоставить вам больше информации. Возможно, MemoryStream.Dispose не работает? Тем самым создается утечка памяти.

...