Подтвердить что ты не робот

Как вы убедитесь, что WPF выпускает большой битмап-источник из памяти?

Система: Windows XP SP3,.NET 3.5, 4 ГБ оперативной памяти, Dual 1,6 гц

У меня есть приложение WPF, которое загружает и переводит (используя анимацию Storyboard) чрезвычайно большие PNG. Эти PNG имеют разрешение 8190x1080. По мере запуска приложения он, похоже, кэширует изображения, и системная память медленно ползет вверх. В конце концов он дросселирует систему и выбрасывает исключение OutOfMemoryException.

Вот шаги, которые я сейчас предпринимаю, чтобы решить эту проблему:

1) Я удаляю объекты BitmapSource из приложения

2) Я устанавливаю BitmapSource BitmapCacheOption в None при загрузке BitmapSource

3) Я замерзаю BitmapSource после его загрузки.

4) Я удаляю все ссылки на изображение, которое использует источник, а также любые ссылки на сам источник.

5) Вручную вызовите GC.Collect() после завершения вышеуказанных шагов.

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

4b9b3361

Ответ 1

Вы, конечно, много работали над этим. Я думаю, что основная проблема заключается в том, что BitmapCacheOption.None не предотвращает кэширование базового BitmapDecoder (ов).

Для этого есть несколько сложных решений, таких как выполнение GC.Collect(), загрузка 300 небольших изображений из 300 разных Uris и вызов GC.Collect() снова, но простой простой:

Вместо загрузки из Uri просто создайте Stream и передайте его в конструктор BitmapFrame:

var source = new BitmapImage();
using(Stream stream = ...)
{
  source.BeginInit();
  source.StreamSource = stream;
  source.CacheOption = BitmapCacheOption.OnLoad;    // not a mistake - see below
  source.EndInit();
}

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

Почему BitmapCacheOption.OnLoad? Это кажется противоречивым, но этот флаг имеет два эффекта: он позволяет кэшировать, если возможно кэширование, и это вызывает нагрузку на EndInit(). В нашем случае кеширование невозможно, поэтому все это приводит к тому, что загрузка происходит немедленно.

Очевидно, вы захотите запустить этот код с вашего потока пользовательского интерфейса, а затем заморозить BitmapSource, чтобы вы могли его переместить.

Вы также можете задаться вопросом, почему я не использовал BitmapCreateOptions.IgnoreImageCache. Помимо того, что кеширование невозможно без какого-либо URI, IgnoreImageCache не полностью игнорирует кеш изображения: он игнорирует его только для чтения. Поэтому, даже если параметр IgnoreImageCache установлен, загруженное изображение все еще вставлено в кеш. Разница в том, что существующее изображение в кеше игнорируется.