OutOfMemoryException @ WriteableBitmap @ фоновый агент

У меня есть приложение для Windows Phone 8, которое использует фоновый агент для:

  1. Получить данные из интернета;
  2. Создайте изображение на основе пользовательского элемента управления, который использует данные из шага 1 в качестве источника данных;
  3. В пользовательском элементе управления у меня есть Grid и StackPanel и некоторые элементы управления текстом и изображением;
  4. Когда некоторые из изображений используют локальные ресурсы из папки установки (/Assets/images/...), один из них, который я использовал в качестве фона, выбирается пользователем из библиотеки фотографий телефона, поэтому мне нужно установить источник с помощью С# код позади.

Однако, когда он работает в фоновом режиме, он получает OutOfMemoryException, некоторые проблемы с устранением неполадок:

  1. Когда я запускаю процесс «спереди», все работает нормально;
  2. Если я прокомментирую ход обновления и создам образ напрямую, он также будет работать нормально;
  3. Если я не устанавливаю фоновое изображение, оно также работает нормально;
  4. OutOfMemoryException был выброшен во время var bmp = new WriteableBitmap(480, 800);
    Я уже уменьшил размер изображения с 1280*768 до 800*480, я думаю, что это нижняя граница для полноэкранного фонового изображения, не так ли?
  5. После некоторых исследований я обнаружил, что эта проблема возникает из-за превышения ограничения в 11 МБ для периодической задачи.
  6. Я попытался использовать DeviceStatus.ApplicationCurrentMemoryUsage для отслеживания использования памяти:

    -- ограничение 11 534 336 (бит)

    -- при запуске фонового агента, даже без какой-либо задачи в нем, использование памяти становится равным 4 648 960

    -- При получении обновления из Интернета оно выросло до 5 079 040.

    -- по завершении оно упало до 4 648 960

    -- Когда начался вызов (для создания изображения из пользовательского элемента управления), он вырос до 8 499 200

Ну, я думаю, в этом проблема, мало памяти для рендеринга изображения через WriteableBitmap.

Любая идея, как решить эту проблему?

Есть ли лучший способ создать изображение из пользовательского элемента управления/или чего-либо еще?

На самом деле исходное изображение может быть всего 100 КБ или около того, однако при рендеринге с помощью WriteableBitmap размер файла (а также объем необходимой памяти, я думаю) может вырасти до 1-2 МБ.

Или я могу освободить память откуда угодно?

==============================================================

Кстати, когда эта статья Code Project говорит, что я могу использовать только 11 МБ памяти в периодической задаче;

Однако эта статья MSDN говорит, что я могу использовать до 20 МБ или 25 МБ с Windows Phone 8 Update 3; Что правильно? И почему я в первой ситуации?

==============================================================

Редактировать:

Говоря об отладчике, он также указан в Статья MSDN:

When running under the debugger, memory and timeout restrictions are suspended.

Но зачем мне все-таки нажимать на ограничение?

==============================================================

Редактировать:

Что ж, я нашел кое-что, похоже, полезное, сейчас я проверю их, предложения по-прежнему приветствуются.

http://writeablebitmapex.codeplex.com/

http://suchan.cz/2012/07/pro-live-tiles-for-windows-phone/

http://notebookheavy.com/2011/12/06/microsoft-style-dynamic-tiles-for-windows-phone-mango/

==============================================================

Код для генерации изображения:

Deployment.Current.Dispatcher.BeginInvoke(() =>
{
    var customBG = new ImageUserControl();
    customBG.Measure(new Size(480, 800));
    var bmp = new WriteableBitmap(480, 800); //Thrown the **OutOfMemoryException**
    bmp.Render(customBG, null);
    bmp.Invalidate();
    using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
    {
        filename = "/Shared/NewBackGround.jpg";
        using (var stream = isf.OpenFile(filename, System.IO.FileMode.OpenOrCreate))
        {
            bmp.SaveJpeg(stream, 480, 800, 0, 100);
        }
    }
}

Код XAML для ImageUserControl:

<UserControl blabla... d:DesignHeight="800" d:DesignWidth="480">
    <Grid x:Name="LayoutRoot">
    <Image x:Name="nBackgroundSource" Stretch="UniformToFill"/>

    //blabla...
    </Grid>
</UserControl>

Код С#, стоящий за ImageUserControl:

public ImageUserControl()
{
    InitializeComponent();
    LupdateUI();
}

public void LupdateUI()
{
    DataInfo _dataInfo = new DataInfo();
    LayoutRoot.DataContext = _dataInfo;
    try
    {
        using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
        {
            using (var isoFileStream = isoStore.OpenFile("/Shared/BackgroundImage.jpg", FileMode.Open, FileAccess.Read))
            {
                BitmapImage bi = new BitmapImage();
                bi.SetSource(isoFileStream);
                nBackgroundSource.Source = bi;
            }
        }
    }
    catch (Exception) { }
}

Когда DataInfo является другим классом в страница настроек, содержащая данные, получаемые из Интернета:

public class DataInfo
{
    public string Wind1 { get { return GetValueOrDefault<string>("Wind1", "N/A"); } set { if (AddOrUpdateValue("Wind1", value)) { Save(); } } }
    public string Wind2 { get { return GetValueOrDefault<string>("Wind2", "N/A"); } set { if (AddOrUpdateValue("Wind2", value)) { Save(); } } }
    //blabla...
}

person Max Meng    schedule 25.12.2013    source источник
comment
Я почти уверен, что у Nokia есть API для этого, который вообще не загружает все изображение в память (они построили его для своего телефона с 41-мегапиксельной камерой, и он должен работать хорошо). В любом случае, на вашем месте я бы не обновлял изображение в фоновом режиме - зачем вам это вообще нужно в агенте, а не просто отмечать «сделать это при загрузке» для приложения?   -  person Benjamin Gruenbaum    schedule 25.12.2013
comment
41-мегапиксельная камера должна работать спереди, верно? Так может дело в другом? Я создаю это изображение для установки в качестве изображения экрана блокировки, поэтому обновлять «вручную» каждый раз при запуске приложения не очень хорошая идея, спасибо   -  person Max Meng    schedule 25.12.2013
comment
Нет, у них есть API, который позволяет вам работать с изображениями, не помещая их все в память — это звучит именно так, как вам нужно. Тем не менее, с точки зрения взаимодействия с пользователем, если я замечу, что ваше приложение замедляет работу моего телефона, я отключу фоновый агент, что всегда возможно для пользователя. Я могу попросить друга из Nokia, но я считаю, что API изображений Nokia — ваш лучший выбор.   -  person Benjamin Gruenbaum    schedule 27.12.2013
comment
@BenjaminGruenbaum, спасибо за эту полезную информацию, звучит хорошо, я найду время, чтобы изучить ее. Кстати, это означает, что мы можем использовать его только на телефоне Nokia?   -  person Max Meng    schedule 27.12.2013
comment
Нет, он работает на всех телефонах с Windows Phone — они только что разработали его для своих, но он работает (и поддерживается) и на других устройствах.   -  person Benjamin Gruenbaum    schedule 27.12.2013
comment
Замечательно! Еще раз спасибо.   -  person Max Meng    schedule 27.12.2013


Ответы (2)


If I comment out the update progress, and create the image directly, it also works fine Я думаю, вам следует сосредоточиться на этой части. Кажется, это указывает на то, что часть памяти не освобождается после обновления. Перед рендерингом изображения убедитесь, что все ссылки, используемые в процессе обновления, выходят за рамки. Принудительная сборка мусора также может помочь:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(); // Frees the memory that was used by the finalizers

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

Тем не менее, я уже был в такой ситуации, поэтому я знаю, что этого может быть недостаточно. Дело в том, что некоторые библиотеки в .NET Framework загружаются лениво. Например, если ваш процесс обновления включает в себя загрузку некоторых данных, то фоновый агент загрузит сетевые библиотеки. Эти библиотеки нельзя выгрузить, и они будут тратить часть памяти вашего агента. Вот почему, даже освободив всю память, которую вы использовали в процессе обновления, вы не вернете тот же объем свободной памяти, который у вас был при запуске фонового агента. Увидев это, я в одном из своих приложений распределил рабочую нагрузку фонового агента на два выполнения. В основном, когда агенты выполняют:

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

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

person Kevin Gosse    schedule 25.12.2013
comment
Спасибо за ответ и все предложения! «Фоновый агент с двумя выполнениями» звучит как умный обходной путь, я возьму его как последний вариант, если не смогу найти лучшее решение. - person Max Meng; 25.12.2013
comment
Отзыв: релиз-режим не помогает. - person Max Meng; 25.12.2013
comment
Ну, я сдаюсь. Что бы я ни пробовал, все заканчивалось на OutOfMemoryException. - person Max Meng; 26.12.2013

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

480x800 добавляет МБ к вашей памяти, потому что для каждого пикселя требуется 4 байта, поэтому в итоге получается около 1,22 МБ. При сжатии в JPEG тогда да — логично, что всего около 100 КБ. Но всякий раз, когда вы используете WriteableBitmap, он загружается в память.

Одна из вещей, которую вы можете попробовать, прежде чем принудительно использовать GC.Collect, как указано в другом ответе, - это обнулить вещи еще до того, как они выйдут за рамки - будь то BitmapImage или WriteableBitmap. Кроме этого, вы можете попробовать удалить объект изображения из сетки программно, когда закончите, и установить его источник также равным нулю.

Есть ли какие-либо другие объекты WriteableBitmap, BitmapImage или Image, которые вы нам не показываете?

Также попробуйте без отладчика. Я где-то читал, что он добавляет еще 1-2 МБ, что много, когда у вас всего 11 МБ. Хотя, если он так быстро вылетает с отладчиком, я бы не стал рисковать, даже если вдруг все нормально без отладчика. Но только для целей тестирования вы можете попробовать.

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

person Igor Ralic    schedule 25.12.2013
comment
Спасибо за ответ! 1.--Periodic agents and resource-intensive agents can use no more than 20 MB of memory at any time on devices with 1 GB of memory or more. On lower-memory devices, the limit is 11 MB. Вот что на этой странице документации. Кажется, 20 МБ не должны ограничивать аудиоагентов? - person Max Meng; 25.12.2013
comment
2. -- Я должен признать, что пользовательский элемент управления немного сложен, у меня есть много <Grid.RowDefinitions>&<Grid.ColumnDefinitions> и еще около 10 небольших изображений, которые используют привязки данных, такие как: <Image Source="{Binding Img1}"/>. Так что больше нет WriteableBitmap или BitmapImage объектов. Я предполагаю, что больше всего памяти потребляет процесс [создать растровое изображение для пользовательского элемента управления] + [отрисовать фоновое изображение] + [получить данные из Интернета]. На самом деле, я пытался закомментировать все элементы в пользовательском элементе управления, кроме фонового изображения, это не помогает. - person Max Meng; 25.12.2013
comment
3.-- На самом деле я надеюсь, что смогу сделать это другим способом, я забыл упомянуть, что я новичок в C# и Win Phone Dev. Может быть, WriteablebitmapEX подойдет? Я попробую некоторые новые подходы, которые я основал после того, как опубликовал свой вопрос. Спасибо за внимание! - person Max Meng; 25.12.2013
comment
Отзыв: релиз-режим не помогает. - person Max Meng; 25.12.2013