OutOfMemoryException @ WriteableBitmap @ background agent
Asked Answered
G

2

7

I have a Windows Phone 8 App, which uses Background agent to:

  1. Get data from internet;
  2. Generate image based on a User Control that uses the data from step 1 as data source;
  3. In the User Control I have Grid & StackPanel & some Text and Image controls;
  4. When some of the Images use local resources from the installation folder (/Assets/images/...) One of them which I used as a background is selected by user from the phone's photo library, so I have to set the source using C# code behind.

However, when it runs under the background, it get the OutOfMemoryException, some troubleshooting so far:

  1. When I run the process in the "front", everything works fine;
  2. If I comment out the update progress, and create the image directly, it also works fine;
  3. If I don't set the background image, it also works fine;
  4. The OutOfMemoryException was thrown out during var bmp = new WriteableBitmap(480, 800);
    I already shrink the image size from 1280*768 to 800*480, I think it is the bottom line for a full screen background image, isn't it?
  5. After some research, I found out this problem occurs because it exceeded the 11 MB limitation for a Periodic Task.
  6. I tried use the DeviceStatus.ApplicationCurrentMemoryUsage to track the memory usage:

    -- the limitation is 11,534,336 (bit)

    -- when background agent started, even without any task in it, the memory usage turns to be 4,648,960

    -- When get update from internet, it grew up to 5,079,040

    -- when finished, it dropped back to 4,648,960

    -- When the invoke started (to generate image from the User Control), it grew up to 8,499,200

Well, I guess that's the problem, there is little memory available for it to render the image via WriteableBitmap.

Any idea how to work out this problem?

Is there a better method to generate an image from a User Control / or anything else?

Actually the original image might only be 100 kb or around, however, when rendering by WriteableBitmap, the file size (as well as the required memory size I guess) might grew up to 1-2MB.

Or can I release the memory from anywhere?

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

BTW, when this Code Project article says I can use only 11MB memory in a Periodic Task;

However, this MSDN article says that I can use up to 20 MB or 25MB with Windows Phone 8 Update 3; Which is correct? And why am I in the first situation?

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

Edit:

Speak of the debugger, it also stated in the MSDN article:

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

But why would I still hit the limitation?

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

Edit:

Well, I found something seems to be helpful, I will check on them for now, suggestions are still welcome.

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/

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

The code to generate the image:

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);
        }
    }
}

The XAML code for the ImageUserControl:

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

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

The C# code behind the 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) { }
}

When DataInfo is another Class within the settings page that hold data get from the internet:

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...
}
Gerhard answered 25/12, 2013 at 7:47 Comment(5)
I'm pretty sure Nokia has an API for this that doesn't load the whole image into memory at all (they built it for their 41MP camera phone and it's suposed to work well). Anyway, if I were you I would not update the image in the background - why do you need that in the agent anyway rather than just mark 'do it when you load' for the application?Jaco
The 41MP camera should run in the front right? So, that might be a different thing? I create this image to set as the lock screen image, so update 'manually' each time launch the app doesn't sound like a good idea, thanksGerhard
No, what they have is an API that lets you work with images without putting it all into memory - that sounds exactly like what you need. That said - from a user experience point of view if I notice your app slows down my phone I'll disable the background agent which is always a possibility the user has. I can ask a friend from Nokia but I believe the Nokia imaging API is your best bet here.Jaco
@BenjaminGruenbaum, thanks for this useful information, sounds good, I'll take time to look into it. BTW, is that means we can only use it on a Nokia phone?Gerhard
No, it works on all Windows Phone phones - they just developed it for theirs but it works (and is supported) on other devices as well.Jaco
P
1

If I comment out the update progress, and create the image directly, it also works fine I think you should focus on that part. It seems to indicate some memory isn't freed after the update. Make sure all the references used during the update process go out of scope before rendering the picture. Forcing a garbage collection can help too:

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

Another thing to consider is that the debugger is also using a lot of memory. Do a real test by compiling your project in "Release" mode and deploying on a phone to make sure you're running out of memory.

Still, I have already been in that situation so I know it may not be enough. The point is: some libraries in the .NET Framework are loaded lazily. For instance, if your update process involves downloading some data, then the background agent will load the network libraries. Those libraries can't be unloaded and will waste some of your agent's memory. That's why, even by freeing all the memory you used during the update process, you won't reach back the same amount of free memory you had when starting the background agent. Seeing this, what I did in one of my app was to span the workload of the background agent across two executions. Basically, when the agents executes:

  • Check in the isolated storage if there's pending data to be processed. If not, just execute the update process and store all needed data in the isolated storage
  • If there is pending data (that is, in the next execution), generate the picture and clear the data

It means the picture will be generated only once every hour instead of once every 30 minutes, so use this workaround only if everything else fail.

Pyorrhea answered 25/12, 2013 at 10:23 Comment(3)
Thanks for the answer and all the suggestions! The 'two executions background agent' sounds like a clever workaround, I'll take it as the last option if I couldn't work out a better solution.Gerhard
Feedback: the release mode doesn't help.Gerhard
Well, I give up. Whatever I tried, it all ended with OutOfMemoryException.Gerhard
S
1

The larger memory limit is for background audio agents, it clearly states that in the documentation. You're stuck with 11 MB, which can really be a big pain when you're trying to do something smart with pictures in the background.

480x800 adds a MB to your memory because it takes 4 bytes for every pixels, so in the end it's around 1.22MB. When compressed in JPEG, then yes - it makes sense that it's only around 100KB. But whenever you use WriteableBitmap, it gets loaded into memory.

One of the things you could try before forcing the GC.Collect as mentioned in another answer is to null things out even before they go out of scope - whether it's a BitmapImage or a WriteableBitmap. Other than that you can try removing the Image object from the Grid programmatically when you're done and setting the Source of it also to be null.

Are there any other WriteableBitmap, BitmapImage or Image objects you're not showing us?

Also, try without the debugger. I've read somewhere that it adds another 1-2MB which is a lot when you have only 11 MB. Although, if it crashes so quickly with the debugger, I wouldn't risk it even if it suddenly seams OK without the debugger. But just for testing purposes you can give it a shot.

Do you need to use the ImageUserControl? Can you try doing step by step creating Image and all the other objects, without the XAML, so you can measure memory in each and every step to see at which point it goes through the roof?

Seismo answered 25/12, 2013 at 12:17 Comment(4)
Thanks for the answer! 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. That's what on that documentation page. It seems the 20MB shouldn't be limit to the audio agents?Gerhard
2.--I have to admit the user control is a little complex, I have a lot of <Grid.RowDefinitions>&<Grid.ColumnDefinitions>, and around 10 more small images, which are using data bindings like: <Image Source="{Binding Img1}"/>. So there is no more WriteableBitmap or BitmapImage objects. I guess the most memory consuming progresses are [create a bitmap for the user control]+[render the background image]+[get data from internet]. Actually, I tried commented out all the element in the user control except the background image, it doesn't help.Gerhard
3.-- Actually I hope I can do it in anther way, I forgot to mention that I'm a newbie to C# & Win Phone Dev. Maybe the WriteablebitmapEX will do the job? I will try some new approaches I founded after I posted my question. Thanks for your attention!Gerhard
Feedback: the release mode doesn't help.Gerhard

© 2022 - 2024 — McMap. All rights reserved.