1

我正在构建一个小型 Windows Phone 8 应用程序(基督教东正教日历),它有一个应该更新动态磁贴的后台代理。该应用程序将需要访问手机中的联系人,因此我选择退出互联网访问,因此后端磁贴生成至少现在是没有问题的。我个人不会相信可以访问我的联系人和互联网的应用程序。

最近我的预定代理(生成三个 PNG)开始对我进行 OutOfMemoryException-ing。始终如一。我使用 DeviceStatus 来查询和调试它的行为。

很难将此称为内存泄漏,因为如果我调用 GC.Collect 在所有三个 tile 代之间它不会抛出 OutOfMemoryException。如果这是一个真正的内存泄漏,一些(大的和/或许多)对象将仍然被其他活动/根对象引用,并且没有任何 GC.Collect 会有所帮助。就我而言,GC.Collect会有所帮助。我可以继续使用 GC.Collect,但我觉得这样做很脏。

当我正在开发免费和开源的应用程序时,您可以在http://orthodoxcalendar.codeplex.com上详细查看项目当前开发状态​​的所有代码

平铺生成包括获取背景并在该背景上叠加另外两个图像。基本上对于我生成的三个PNG中的每一个

var bytes1 = (byte[])resourceManager.GetObject(resourceName1);
var stream1 = new MemoryStream(bytes);

var bytes2 = (byte[])resourceManager.GetObject(resourceName2);
var stream2 = new MemoryStream(bytes);

var bytes3 = (byte[])resourceManager.GetObject(resourceName3);
var stream3 = new MemoryStream(bytes);

var writeableBitmap1 = BitmapFactory.New(size.Width, size.Height).FromStream(stream1); // background
var writeableBitmap2 = BitmapFactory.New(size.Width, size.Height).FromStream(stream2); // first overlay
var writeableBitmap3 = BitmapFactory.New(size.Width, size.Height).FromStream(stream3); // second overlay

writeableBitmap1.Blit(new Point(0, 0), writeableBitmap2, new Rect(0, 0, width2, height2), Colors.White, BlendMode.Alpha);
writeableBitmap1.Blit(new Point(0, 0), writeableBitmap3, new Rect(0, 0, width3, height3), Colors.White, BlendMode.Alpha);
writeableBitmap1.DrawText("Some text", new Point(5, 139), Color.Black, 17);
writeableBitmap1.Invalidate(); // flatten things

using(var outputStream = new WhateverStream())
{
  PNGWriter.Write(writeableBitmap1, outputStream);
}

writeableBitmap1.SetSource(new MemoryStream(MiscData.MinimumPng)); // set the writeable bitmap to a 1x1 transparent PNG to, hopefully, force it to release unamanaged memory or other stuff
writeableBitmap2.SetSource(new MemoryStream(MiscData.MinimumPng));
writeableBitmap3.SetSource(new MemoryStream(MiscData.MinimumPng));

stream1.Dispose();
stream2.Dispose();
stream3.Dispose();

如果您要查看项目,代码与上面的代码并不完全相同,因为我已经将几乎所有依赖项都包装在适配器和提取的接口中。跨越许多程序集。上面的代码是简化版本,我认为是相关的代码行。

上面代码的一些解释:

  • 所有这些代码都在 Dispatcher.BeginInvoke 内的后台代理中运行,因为除了 UI 线程之外,您似乎无法在任何其他线程上操作 WritableBitmap
  • PNG 数据作为 resx 存储在另一个程序集中。我知道这会使程序集变胖,但我需要它来跨平台重用它,因为程序集是 PCL
  • 直接使用字节数组创建 WriteableBitmap 似乎以一种神秘的方式失败,所以我将它包装在 MemoryStream 中,并且以某种方式,它可以工作
  • PNG writer 取自ToolStack
  • 预生成图像是不可行的,因为有多个版本的“第一叠加”、“第二叠加”以及主要是“某些文本”。这至少意味着成千上万的图像。

问题的核心:我是否在做一些我不知道的非常错误的事情?我唯一想到的是 JPEG 的生成速度更快,内存消耗更少,但它们没有我想要的透明度。这实际上可以称为内存泄漏吗?


后期编辑:似乎经过更多调试后,它的行为从上面的行为改变为真正的内存泄漏。我从 PNG 生成切换到 JPEG 生成,现在内存较低。输入图像仍然是 PNG,但在另一端会吐出 JPEG。内存占用比之前的阈值低了几兆字节。


第二次编辑:我将逻辑放在按钮上的 10.000 重复循环中,似乎没有太多的内存消耗。我开始认为实际上并没有内存泄漏,而只是在生成过程中消耗了更高的内存,这足以让脆弱的代理崩溃。

4

1 回答 1

1

在做类似的事情时,我必须在调用 GC.Collect 之前将 writeablebitmaps 显式设置为 null(尽管应该是不必要的)。

此外,最好依次创建和销毁(和收集)每个图像,而不是先创建所有图像然后再销毁它们。这将有助于解决任何一点的开销。

另请注意,在调试器中跟踪内存使用情况时,调试器会增加大约 3mb 的开销,而这些开销在运行时是看不到的。

于 2013-11-06T11:03:03.097 回答