c# - WriteableBitmap Утечка памяти?




windows-phone live-tile (5)

я получаю то же исключение при конвертации uielement в writeablebitmap в Taskagent. Я просто пытался так много раз. Я нашел решение получить работать.

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

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

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

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 (rectangle, rectangle.RenderTransform);

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

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

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

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


Проблема заключается в том, что rectangle.RenderTransform является экземпляром объекта, и если вы установили writableBitmap для обнуления объекта rectangle.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();
}

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

до:

после:


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

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

наконец, пункт;


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


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

Код, в который я попал, выглядит следующим образом:

public class HideableControl<T>: Control where T: class
{
    private string _propertyName;
    private PropertyInfo _propertyInfo;

    public string PropertyName
    {
        get { return _propertyName; }
        set
        {
            _propertyName = value;
            _propertyInfo = typeof(T).GetProperty(value);
        }
    }

    protected override bool GetIsVisible(IRenderContext context)
    {
        if (_propertyInfo == null)
            return false;

        var model = context.Get<T>();

        if (model == null)
            return false;

        return (bool)_propertyInfo.GetValue(model, null);
    }

    protected void SetIsVisibleProperty(Expression<Func<T, bool>> propertyLambda)
    {
        var expression = propertyLambda.Body as MemberExpression;
        if (expression == null)
            throw new ArgumentException("You must pass a lambda of the form: 'vm => vm.Property'");

        PropertyName = expression.Member.Name;
    }
}

public interface ICompanyViewModel
{
    string CompanyName { get; }
    bool IsVisible { get; }
}

public class CompanyControl: HideableControl<ICompanyViewModel>
{
    public CompanyControl()
    {
        SetIsVisibleProperty(vm => vm.IsVisible);
    }
}

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

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







c# windows-phone writeablebitmap live-tile