Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Bitmap.Save, огромная утечка памяти

Автор вопроса: David AndersonОтветов: 5

У меня есть приложение, в котором я беру растровое изображение, сжимаю его с помощью 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);
                    }
                }
            }
        }
    }
  • Вы сами смотрели на источник с помощью Reflector? matt b 16.05.2009

memory-leaksbitmap

Ответы

Ответ 1 · рейтинг 4 · Katelyn Gadd

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

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

Ответ 2 · рейтинг 3 · David Anderson

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

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

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

Ответ 3 · рейтинг 2 · Demi

Вы должны установить для растрового изображения значение null после завершения работы с ним после удаления.

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

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

Ответ 4 · kevindaub

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

Ответ 5 · Wayne Hartman

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

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы