Утечка памяти при загрузке изображения с помощью C#

StackOverflow https://stackoverflow.com/questions/1714841

Вопрос

У меня проблема с утечкой памяти в моем приложении, которое загружает большое количество изображений.Я довольно новичок в C # и думал, что мои дни проблем с утечкой памяти прошли.Я не могу разобраться в проблеме - может быть, я использую какие-то неуправляемые модули, с которыми я неправильно обращаюсь?

Чтобы проиллюстрировать свою проблему, я упростил суть причины проблемы и перенес это в чистый проект.Обратите внимание, что это все глупый код, который не отражает оригинальное приложение, из которого он был взят.В тестовом приложении у меня есть 2 кнопки, запускающие два события.

Кнопка 1 - Создать:Установка объекта в datacontext.Это загрузит изображения и сохранит их работоспособность, установив для объекта значение DataContext:

var imgPath = @"C:\some_fixed_path\img.jpg";
DataContext = new SillyImageLoader(imgPath);

Кнопка 2 - Очистка:Насколько я понимаю, если я отпущу ссылку, содержащую SillyImageLoader, которая снова содержит изображения, то это будет удалено.Я также явно запускаю сборку мусора, просто чтобы сразу увидеть объем памяти после удаления ссылки.

DataContext = null; 
System.GC.Collect();

При тестировании я загружаю изображение в формате jpeg размером 974 КБ.Хранение 30 растровых представлений этого увеличивает использование памяти моим приложением с ~ 18 МБ до ~ 562 МБ.ОК.Но когда я нажимаю кнопку очистки, объем памяти уменьшается только до ~ 292 МБ.Если я повторю Create + CleanUp, у меня останется еще ~ 250 МБ памяти.Так что очевидно, что кто-то все еще чем-то владеет.

Вот SillyImageLoader-код:

namespace MemoryLeakTest
{
    using System;
    using System.Drawing;
    using System.Windows;
    using System.Windows.Interop;
    using System.Windows.Media.Imaging;

    public class SillyImageLoader
    {
        private BitmapSource[] _images; 

        public SillyImageLoader(string path)
        {
            DummyLoad(path);
        }

        private void DummyLoad(string path)
        {
            const int numberOfCopies = 30;
            _images = new BitmapSource[numberOfCopies];

            for (int i = 0; i < numberOfCopies; i++)
            {
                _images[i] = LoadImage(path);
            }
        }

        private static BitmapSource LoadImage(string path)
        {
            using (var bmp = new Bitmap(path))
            {
                return Imaging.CreateBitmapSourceFromHBitmap(
                    bmp.GetHbitmap(),
                    IntPtr.Zero,
                    Int32Rect.Empty,
                    BitmapSizeOptions.FromEmptyOptions());
            }            
        }
    }
}

Есть какие-нибудь идеи?Похоже, проблема связана с BitmapSource.При хранении только растрового изображения утечки памяти нет.Я использую BitmapSource, чтобы иметь возможность установить это в качестве исходного свойства изображения.Должен ли я сделать это по-другому?Если это так - я все равно хотел бы знать ответ на утечку памяти.

Спасибо.

Это было полезно?

Решение

Когда ты позвонишь

bmp.GetHbitmap()

создается копия растрового изображения.Вам нужно будет сохранить ссылку на указатель на этот объект и вызвать

DeleteObject(...)

на нем.

От здесь:

Замечания

Вы несете ответственность за вызов метода GDI DeleteObject для освобождения памяти, используемой растровым объектом GDI.


Возможно, вы сможете избавить себя от головной боли (и накладных расходов) при копировании растрового изображения, используя Растровое изображение вместо BitmapSource.Это позволяет загружать и создавать файлы за один шаг.

Другие советы

Вам нужно позвонить в GDI DeleteObject метод на основе IntPtr указатель, возвращенный из GetHBitmap().Тот Самый IntPtr возвращаемый из метода указатель на копию объекта в памяти.Это должно быть освобождено вручную с помощью следующего кода:

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

private static BitmapSource LoadImage(string path)
{

    BitmapSource source;
    using (var bmp = new Bitmap(path))
    {

        IntPtr hbmp = bmp.GetHbitmap();
        source = Imaging.CreateBitmapSourceFromHBitmap(
            hbmp,
            IntPtr.Zero,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());

        DeleteObject(hbmp);

    }

    return source;
}

Кажется, что когда вы вызываете GetHBitmap(), вы несете ответственность за освобождение объекта.

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

private void DoGetHbitmap() 
{
    Bitmap bm = new Bitmap("Image.jpg");
    IntPtr hBitmap = bm.GetHbitmap();

    DeleteObject(hBitmap);
}

Я предполагаю, что BitmapSource не берет на себя ответственность за освобождение этого объекта.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top