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

Записывать утечку памяти в битрейме?

Я использую приведенный ниже код для создания живой плитки, основанной на элементе пользовательского интерфейса. Он отображает uiElement на WriteableBitmap, сохраняет битмап +, возвращает имя файла. Этот метод запускается в операторе заданий фона телефона Windows, и я бегу в пределы памяти.

private string CreateLiveTileImage(Canvas uiElement, int width, int heigth)
{
   var wbmp = new WriteableBitmap(width, heigth);
   try
   {
      wbmp.Render(uiElement, null);
      wbmp.Invalidate();

      var tileImageName = _liveTileStoreLocation;
      using (var stream = new IsolatedStorageFileStream(tileImageName, FileMode.Create, FileAccess.Write, IsolatedStorageFile.GetUserStoreForApplication()))
      {
         wbmp.SaveJpeg(stream, width, heigth, 0, 100);
         stream.Close();
      }

      uiElement = null;
      wbmp = null;
      GC.Collect();
      return "isostore:" + tileImageName;
   }
   catch (Exception exception)
   {
      // ...
   }
   return null;
}

Я провел некоторое тестирование, и проблема в том, что эти методы утечки памяти, но я не знаю почему/где?!

Я также проделал некоторые тестовые прогоны - перед первым запуском этого метода:

Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
7.249.920 Bytes

Это нормально, так как отладчик подключен, который использует около 2 МБ памяти.

Выполнение еще нескольких прогонов этого метода (снова установите метод запуска в отладчик):

Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
8851456  long +  40960
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
8892416  long + 245760
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9138176  long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9281536  long + 151552
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9433088  long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9576448  long + 139264
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9715712  long + 139264
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9859072  long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
10006528 long + 147456

Таким образом, память, используемая этим методом, увеличивается.

Но почему? По-моему, нет ссылок, которые мешают собирать объекты.

ОБНОВЛЕНИЕ 04.05.2013

Привет,

Благодарю вас за все ваши ответы! Как было предложено, я уменьшил код +, в конце концов, смог воспроизвести проблему в нескольких строках кода.

void Main()
{
   for (int i = 0; i < 100; i++)
   {
      CreateImage();
   }
}

private void CreateImage()
{
   var rectangle = CreateRectangle();
   var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);

   rectangle = null;
   writeableBitmap = null;
   GC.Collect();
}

private Rectangle CreateRectangle()
{
   var solidColorBrush = new SolidColorBrush(Colors.Blue);
   var rectangle = new Rectangle
   {
   Width = 1000,
   Height = 1000,
   Fill = solidColorBrush  // !!! THIS causes that the image writeableBitmap never gets garbage collected
   };

   return rectangle;
}

После запуска приложения: ApplicationCurrentMemoryUsage: "11 681 792 байт"

1 Итерация - ApplicationCurrentMemoryUsage: "28 090 368 байт"

5 Итерации - ApplicationCurrentMemoryUsage: "77 111 296 байт"

20 Итерации - ApplicationCurrentMemoryUsage: "260 378 624 байтов"

После 23 итераций: исключение из памяти. Ln.: var writeableBitmap = new WriteableBitmap (прямоугольник, прямоугольник .RenderTransform);

Только комментируя строку "Fill = solidColorBrush", метод CreateImage() был вызван 100 раз без проблем - после 100-й итерации использование памяти было около "16 064 512 байтов".

Итак, проблема в кисти! Когда используется для заполнения элемента пользовательского интерфейса, и позже этот элемент пользовательского интерфейса отображается на записываемом растровом изображении, битмап никогда не получает сбор мусора.

Конечно, это не имеет никакого смысла, на мой взгляд. Кисть выходит из области, поэтому она тоже должна быть собрана мусором! (Установка кисти в значение null после ее использования ничего не изменила)

Многие элементы моего пользовательского интерфейса используют кисть для заполнения, поэтому я не могу просто удалить использование кистей. Что вы думаете об этой проблеме?

4b9b3361

Ответ 1

Проблема заключается в том, что rectangle.RenderTransform является экземпляром объекта, и если вы устанавливаете writeableBitmap для null прямоугольника. Объект RenderTransform все еще жив и содержит прямоугольник в памяти... поэтому решение заключается в редактировании кода как следует:

private void CreateImage()
{
   var rectangle = CreateRectangle();
   var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);

   rectangle.RenderTransform = null; //and your memory would be happy ;)
   rectangle = null;
   writeableBitmap = null;
   GC.Collect();
}

см. скриншоты памяти...

перед:

memory without setting rectangle.RenderTransform to null

после:

memory with setting rectangle.RenderTransform to null

Ответ 2

Наконец, в try catch должен быть wbmp = null для начала.

И вы его визуализируете, что означает, что вы привязываете этот объект к внешнему (функциональному) списку. Поэтому никакое количество GC.Collect фактически не будет собирать его, поскольку оно все еще "в игре".

Установите источник uielement в значение null, что может избавиться от привязанной ссылки, почему GC игнорирует ее.

Ответ 3

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

Ответ 4

Я получаю такое же исключение при преобразовании uielement в writeablebitmap в taskagent. просто попробовал столько раз. Я обнаружил, что сольдукция будет работать.

wbmp.SaveJpeg(stream, width, heigth, 0, 60);

вы можете изменить качество до 60 при сохранении uielement в потоке. это хорошо уменьшает использование памяти. вы можете попробовать.

Ответ 5

Попробуйте поставить эту часть:

      uiElement = null;
      wbmp = null;
      GC.Collect();

в finally;