OutOfMemoryException @ WriteableBitmap @后台代理

时间:2013-12-25 07:47:56

标签: c# windows-phone-8 writeablebitmap

我有一个Windows Phone 8应用程序,它使用后台代理:

  1. 从互联网上获取数据;
  2. 基于使用步骤1中的数据作为数据源的用户控件生成图像;
  3. 在用户控件中我有Grid& StackPanel&一些文字和图像控件;
  4. 当某些图像使用安装文件夹中的本地资源时(/ Assets / images / ...) 用户从手机的照片库中选择了我用作背景的其中一个,因此我必须使用C#代码设置源代码。
  5. 然而,当它在后台运行时,它会得到OutOfMemoryException,到目前为止进行了一些故障排除:

    1. 当我在“前面”运行过程时,一切正常;
    2. 如果我注释掉更新进度,并直接创建图像,它也可以正常工作;
    3. 如果我没有设置背景图片,它也可以正常工作;
    4. OutOfMemoryException期间var bmp = new WriteableBitmap(480, 800);被抛弃了 我已经将图像尺寸从1280 * 768缩小到800 * 480,我认为它是全屏背景图像的底线,不是吗?
    5. 经过一些研究后,我发现出现此问题是因为它超出了周期性任务的11 MB限制。
    6. 我尝试使用DeviceStatus.ApplicationCurrentMemoryUsage来跟踪内存使用情况:

      - 限制是11,534,336(位)

      - 当后台代理启动时,即使没有任何任务,内存使用量也变为4,648,960

      - 从互联网获取更新时,它增长到5,079,040

      - 完成后,它回落到4,648,960

      - 当调用开始时(从用户控件生成图像),它长大到8,499,200

    7. 嗯,我猜这就是问题所在,通过WriteableBitmap呈现图片的内存很少。

      知道如何解决这个问题吗?

      是否有更好的方法从用户控件/或其他任何内容生成图像?

      实际上原始图像可能只有100 kb左右,但是,当按WriteableBitmap渲染时,文件大小(以及我猜想的所需内存大小)可能会增长到1-2MB。

      或者我可以从任何地方释放内存吗?

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

      BTW,当这个Code Project article说我在周期性任务中只能使用11MB内存时;

      但是,MSDN article表示我可以使用Windows Phone 8 Update 3最多20 MB或25 MB; 哪个是对的?为什么我处于第一种情况?

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

      编辑:

      说到调试器,它也在MSDN article中说明:

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

      ImageUserControl

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

      ImageUserControl后面的C#代码:

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

      DataInfosettings page中保存数据的另一个类来自互联网:

      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...
      }
      

2 个答案:

答案 0 :(得分:1)

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分钟生成一次,所以只有在其他一切都失败时才使用此解决方法。

答案 1 :(得分:1)

较大的内存限制适用于后台音频代理,它在文档中明确指出。你坚持使用11 MB,当你在后台尝试使用智能手机时,这真的很痛苦。

480x800为你的内存增加了一个MB,因为每个像素需要4个字节,所以最后它大约是1.22MB。当用JPEG压缩时,那么是 - 它只有大约100KB才有意义。但无论何时使用WriteableBitmap,它都会被加载到内存中。

在另一个答案中提到的强制GC.Collect之前你可以尝试的其中一件事就是在它们超出范围之前将其归零 - 无论是BitmapImage还是WriteableBitmap。除此之外,您可以尝试以编程方式从网格中删除Image对象,并将其设置为null。

你有没有向我们展示任何其他的WriteableBitmap,BitmapImage或Image对象?

另外,请尝试不使用调试器。我已经阅读了某个地方,它增加了另外1-2MB,当你只有11 MB时,它会很多。虽然,如果它与调试器一起快速崩溃,即使它在没有调试器的情况下突然接合,我也不会冒险。但是出于测试目的,你可以试一试。

您需要使用ImageUserControl吗?您是否可以尝试逐步创建图像和所有其他对象,而不使用XAML,这样您就可以在每个步骤中测量内存以查看它在哪个点穿过屋顶?