Проблемы с памятью .NET при загрузке ~ 40 изображений, память не восстанавливается, возможно, из-за фрагментации LOH - PullRequest
32 голосов
/ 08 июня 2011

Что ж, это мой первый набег в память для профилирования приложения .NET (я уже выполнил настройку процессора), и здесь я немного наткнулся на стену.

У меня есть представление в моем приложении, которое загружает 40 изображений (максимум) на страницу, каждое из которых занимает около 3 МБ.Максимальное количество страниц - 10. Поскольку я не хочу хранить в памяти одновременно 400 изображений или 1,2 ГБ, я устанавливаю для каждого изображения значение NULL при изменении страницы.

Теперь сначалаЯ подумал, что я просто должен иметь несвежие ссылки на эти изображения.Я скачал профилировщик ANTS (отличный инструмент BTW) и провел несколько тестов.График времени жизни объекта говорит мне, что у меня нет никаких ссылок на эти изображения, кроме единственной ссылки в родительском классе (что по замыслу также подтверждено тщательным прочесыванием моего кода):

enter image description here

Родительский класс SlideViewModelBase навсегда остается в кэше, но свойство MacroImage устанавливается равным нулю при изменении страницы.Я не вижу никаких признаков того, что эти объекты должны храниться дольше, чем ожидалось.

Затем я взглянул на кучу больших объектов и использование памяти в целом.После просмотра трех страниц изображений у меня выделено 691,9 МБ неуправляемой памяти и 442,3 МБ на LOH.System.Byte[], который происходит от моего System.Drawing.Bitmap в BitmapImage преобразования, занимает почти все пространство LOH.Вот мой код конверсии:

public static BitmapSource ToBmpSrc( this Bitmap b )
{
    var bi = new BitmapImage();
    var ms = new MemoryStream();
    bi.CacheOption = BitmapCacheOption.OnLoad;
    b.Save( ms,  ImageFormat.Bmp );
    ms.Position = 0;
    bi.BeginInit();
    ms.Seek( 0, SeekOrigin.Begin );
    bi.StreamSource = ms;
    bi.EndInit();
    return bi;
}

Мне трудно найти, куда идет вся эта неуправляемая память.Сначала я заподозрил System.Drawing.Bitmap объекты, но ANTS не показывает их слипание, и я также провел тест, в котором я был абсолютно уверен, что все они были уничтожены, и это не имело никакого значения.Поэтому я еще не выяснил, откуда взялась вся эта неуправляемая память.

Мои две текущие теории:

  1. фрагментация LOH.Если я отойду от постраничного представления и нажму пару кнопок, будет восстановлена ​​половина из ~ 1,5 ГБ.Все еще слишком много, но, тем не менее, интересно.
  2. Какая-то странная вещь, связанная с WPF.Мы используем привязку данных для отображения этих изображений, и я не являюсь экспертом во всех подробностях того, как работают эти элементы управления WPF.

Если у кого-то есть какие-либо теории или советы по профилированию, я был бы очень признателен(конечно) мы в сжатые сроки, и я немного карабкаюсь, чтобы завершить эту последнюю часть и работать.Я думаю, что я был избалован отслеживанием утечек памяти в C ++ ... кто бы мог подумать?

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

Ответы [ 2 ]

35 голосов
/ 08 июня 2011

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

Метод Dispose этого класса-оболочки должен освободить упакованный поток, чтобы его можно было собирать мусором. Как только BitmapImage инициализируется этим потоком-оберткой, поток-обертка может быть удален, освобождая нижележащий поток и позволяя освободить сам большой массив байтов.

BitmapImage сохраняет ссылку на исходный поток, поэтому он поддерживает объект MemoryStream живым. К сожалению, несмотря на то, что MemoryStream.Dispose был вызван, он не освобождает байтовый массив, который переносит поток памяти. Таким образом, в этом случае растровое изображение ссылается на поток, который ссылается на буфер, который может занимать много места в куче больших объектов. Нет настоящей утечки памяти; когда больше нет ссылок на растровое изображение, все эти объекты (в конечном итоге) будут собираться мусором. Но поскольку растровое изображение уже сделало свою собственную частную копию изображения (для рендеринга), кажется довольно расточительным иметь ненужную теперь оригинальную копию растрового изображения в памяти.

Кроме того, какую версию .NET вы используете? До .NET 3.5 SP1 существовала проблема, когда BitmapImage мог вызвать утечку памяти . Обходной путь должен был вызвать Freeze на BitmapImage.

2 голосов
/ 08 июня 2011

Где вы закрываете и утилизируете поток памяти? Вполне возможно, что сборщик мусора должен работать намного усерднее, чтобы освободить ресурсы путем перемещения на несколько поколений вверх, прежде чем выполнять деструкторы на объекте (которые обычно вызывают удаление, если вы забыли это сделать).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...