Утечка памяти WPF WriteableBitmap?

Пытаюсь понять, как освободить память WriteableBitmap.

В следующем разделе кода я заполняю задний буфер WriteableBitmap действительно большим объемом данных из «BigImage» (3600 * 4800 пикселей, только для тестирования). Если я прокомментирую строки, в которых растровое изображение и изображение равны нулю, память это не релиз, и приложение потребляет ~ 230 МБ, даже если изображение и растровое изображение больше не используются!

Как вы можете видеть в конце кода, необходимо вызвать GC.Collect (), чтобы освободить память.

Итак, вопрос в том, как правильно освободить память, используемую объектом WriteableBitmap? GC.Collect () - единственный способ?

Любая помощь была бы замечательной.

PS. Извините за мой плохой английский.

private void buttonTest_Click(object sender, RoutedEventArgs e)
{
            Image image = new Image();
            image.Source = new BitmapImage(new Uri("BigImage"));

            WriteableBitmap bitmap = new WriteableBitmap(
                (BitmapSource)image.Source);

            bitmap.Lock();

            // Bitmap processing

            bitmap.Unlock();

            image = null;
            bitmap = null;

            GC.Collect();
}

person Mario    schedule 26.08.2009    source источник
comment
stackoverflow.com / questions / 1534983 /   -  person nyxtom    schedule 12.03.2010


Ответы (3)


Вы проводите свой тест в Windows XP, используя .Net 3.5 SP1? Если да, то это известная проблема, которая будет исправлена ​​в версии 4.0.

См. http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5d88cdf1-e992-4ad4-8f56-b5dbf92dcf1c.

person Bjarke    schedule 08.11.2009

В общем, память в конечном итоге должна освобождаться автоматически по мере необходимости.

Однако для того, чтобы это произошло, вы должны быть уверены, что объект действительно не используется: никакие ссылки на объект не могут существовать где-либо, включая ссылки, которые «больше не используются». Так, в частности, если вы поместите свой WriteableBitmap и исходный BitmapSource в переменные долгоживущего класса, они не будут выпущены, пока контейнер не будет.

Кроме того, WPF использует сохраненную модель GFX: при рендеринге вы фактически просто сохраняете инструкции по способу рендеринга. «Инструкции» о том, как визуализировать растровое изображение, включают ссылку на растровое изображение - поэтому, если вы когда-либо визуализируете большое растровое изображение, то на некоторое время (по крайней мере, пока оно на экране - даже если версия на экране крошечная) изображения будут сохранены.

На практике; хранить ссылки на эти растровые изображения только там, где они необходимы, и если контекст, в котором они живут, является долгоживущим (длинный вызов метода или вызов метода, генерирующий замыкание со ссылкой на растровые изображения, или член длинного live class), а затем установите для них значение null, если они больше не нужны.

Нет необходимости вручную освобождать память; GC.Collect () должен быть лишним. Как показывает практика, используйте GC.Collect во время тестирования производительности, только чтобы получить представление о потреблении памяти и / или начать с чистого листа. Общая производительность обычно снижается из-за вызовов функции GC.Collect ().

person Eamon Nerbonne    schedule 26.08.2009

Принуждение GC без установки image и bitmap на null не очистит их, потому что на них по-прежнему ссылаются локально и, таким образом, они считаются корневыми ссылками. Это не имеет ничего общего с WriteableBitmap и более, а вопрос о том, как работает сборщик мусора.

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

person Kent Boogaart    schedule 26.08.2009
comment
Спасибо вам обоим за вашу помощь. Проблема в том, что сборщик мусора, похоже, не собирает память, используемую изображениями и растровыми изображениями. Если я прокомментирую строки, в которых им присвоено значение null, и после завершения метода память, используемая всем приложением, останется ~ 234 МБ. Похоже на действительно неприятное поведение. Еще раз спасибо. - person Mario; 26.08.2009
comment
Как долго остается? Вы уверены, что сборщик мусора действительно пытается его собрать? Откройте perfmon и посмотрите счетчики GC, чтобы узнать, действительно ли он пытается собирать данные. Если нет, вам не о чем беспокоиться - памяти нет, поэтому сборщик мусора не требуется. - person Kent Boogaart; 26.08.2009
comment
Спасибо за вашу помощь и терпение. Я видел счетчики производительности сборщика мусора, и в сборке мусора тратится мало времени, однако мне нужна некоторая помощь в выяснении следующих данных: # Байт во всех кучах 1,115,176,000 #GC обрабатывает 845,000 #induce GC 5.0 Похоже, что призыв к GC.Collect () в этом нет необходимости, но нулевое назначение обоих объектов (изображения и растрового изображения) есть, поскольку, когда они удаляются, используемая память остается на уровне ~ 234 МБ, пока приложение не будет закрыто. - person Mario; 26.08.2009
comment
Для сбора мусора ссылки на изображения должны быть перезаписаны (например, пустой ссылкой) или должны перестать существовать (скажем, когда метод завершается). Сборщик мусора не детерминирован - не беспокойтесь о большем, чем эти основные правила. Если память может быть освобождена явным GC.Collect (), все в порядке - позвольте GC определять время и беспокоиться о проблемах с памятью, как только вы столкнетесь с ними. - person Eamon Nerbonne; 28.08.2009