Учечка памяти при использовании BitmapImage.

WPF
  1. 5 года назад

    Здравствуйте. Столкнулся с проблемой, объясню на простом примере.
    Есть вторичное окно с Image. При появлении, окно создаёт BitmapImage из изображения, путь которого передаётся конструктору окна, а затем устанавливает BitmapImage как Source у Image:

    private string _path; //путь к файлу
    ...
    var bitmapImage = new BitmapImage();
    using (var stream = File.OpenRead(_path)) // получаю FileStream и устанавливаю как StreamSource у BitmapImage
    {
        bitmapImage.BeginInit();
        bitmapImage.StreamSource = stream;
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.EndInit();
        stream.Close();
    }
    bitmapImage.Freeze();
    
    Image.Source = bitmapImage;

    Окно появляется с изображением всё хорошо, но после закрытия окна ресурсы используемые ранее не освобождаются и приложение постепенно начинает использовать в пустую всё больше и больше памяти. С учётом того что изображения бывают не малого размера используемая память может вполне увеличиваться на 25МГ за одно открытие окна!!!!!
    Может кто нибудь сталкивался с данной проблемой и знает как её решить или по крайней мере уменьшить)) Буду весьма признателен.

  2. Alexandr_Erohin

    Nov 26 Администратор
    Добавлено 5 года назад Alexandr_Erohin

    В какой системе тестите? В Win 8.1 есть баг с утечкой памяти при работе с 8-ми битными растровыми изображениями (win 7/8 нет такого бага), вот ссылка "BitmapSource fails with OutOfMemoryException" , решается с помощью преобразования в 24-битные растровые изображния:

    Bitmap image = new Bitmap(@"C:\my_bit8.bmp");
    Bitmap bitmap = new Bitmap(image.Width, image.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    
    for (int i = 0; i < image.Width; i++)
    {
                    for (int j = 0; j < image.Height; j++)
                    {
                        Color temp = image.GetPixel(i, j);
                        bitmap.SetPixel(i, j, temp);
                    }
    }
    
    // .....
  3. Добавлено 5 года назад George777

    Здравствуйте, тестирую на Win7. Спасибо за совет, попробовал на всякий случай - но результат отрицательный. Тем более что попиксельное составление Bitmap с разрешением в 4K не ускоряет работу программы)))
    Касательно примера, который я указывал в первом сообщении, замечено что при первом закрытии окна через секунд так 10, сборщик мусора всё же освобождает ресурсы, но вот при повторных открытиях/закрытиях он этого не делает.
    Как вариант программно уменьшать разрешение BitmapImage в момент инициализации. Памяти расходуется намного меньше, но ресурсы так и не освобождаются после закрытия окна.

    var image = new BitmapImage();
    image.BeginInit();
    image.CacheOption = BitmapCacheOption.OnLoad;
    image.UriSource = uri;
    image.DecodePixelHeight = Convert.ToInt32(newHeight);
    image.DecodePixelWidth = Convert.ToInt32(newWidth);
    image.EndInit();
    image.Freeze();

    Ссылка на статью - в статье описано, что данный баг уместен на Net3.0, потом мол исправили, но мне от этого лучше не стало))

    Необходимо решение, позволяющее оптимально распоряжаться ресурсами.

  4. Добавлено 5 года назад George777

    Нашёл решение(скорее всего локальное) для моей проблемы!
    Само приложение - это просмотрщик фотографий(Page) с ListBox-ом в качестве превью.
    При выбора фото в ListBox, основной Image отображает фотографию в оригинальном разрешении. На Page_Unloaded я делал следующее(не рабочий вариант):

    Image.Source = null;
    ImagesList.ItemsSource = null;
    GC.Collect(0);
    GC.GetTotalMemory(true); //Принудительная сборка мусора

    Ссылка на BitmapImage у Image всё таки сохранялась и сборка мусора ни к чему не привадила. Но однажды вручную отменив выделение элемента у ListBox (Image.Source автоматически установилась в null), закрыв страницу всё сработало. Соответственно, затем устанавливая Image.Source = null до вызова события Page_Unloaded сборщик работает как надо))
    Теперь вызов GC.Collect() только заставляет в момент закрытия страницы очищать память, хотя и без этого срабатывает как сказано через секунд 20. Т.е. главным было оборвать связь между Image и BitmapImage до Page_Unloaded))

или зарегистрируйтесь чтобы ответить