WriteableBitmap内存泄漏?

我正在使用下面的代码创建一个基于UI元素的实时图块。 它在WriteableBitmap上呈现uiElement ,保存位图+返回文件名。 此方法在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 MB的内存。

再做一些此方法的运行(在调试器中再次设置为run方法):

 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 

所以这种方法使用的内存增加了。

但为什么? 在我看来,没有任何参考可以防止收集对象。

更新于2013年5月4日

嗨,

谢谢你的所有答案! 正如所建议的,我减少了代码+最终能够在几行代码中重现问题。

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

启动App后:ApplicationCurrentMemoryUsage:“11 681 792 Bytes”

1迭代 – ApplicationCurrentMemoryUsage:“28 090 368 Bytes”

5迭代 – ApplicationCurrentMemoryUsage:“77 111 296 Bytes”

20迭代 – ApplicationCurrentMemoryUsage:“260 378 624 Bytes”

23次迭代后:内存exception。 Ln。:var writeableBitmap = new WriteableBitmap(rectangle,rectangle.RenderTransform);

只有通过注释掉“Fill = solidColorBrush”这一行,CreateImage()方法才会被调用100次而没有任何问题 – 在第100次迭代之后,内存使用量约为“16 064 512字节”。

所以看来问题就是刷! 当用于填充UI元素,并且稍后此UI元素在可写位图上呈现时,位图永远不会被垃圾收集。

当然,这在我看来毫无意义。 刷子超出了范围,所以它也应该被垃圾收集! (在使用后将画笔设置为null并没有改变任何东西)

我的许多UI元素都使用画笔来填充,所以我不能简单地删除画笔的用法。 你怎么看待这个问题?

问题是rectangle.RenderTransform是一个对象的实例,如果你将writableBitmap设置为null,那么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(); } 

看内存截图…

之前:

内存没有设置rectangle.RenderTransform为null

后:

设置矩形的内存.RenderTransform为null

你最后在try catch中应该是wbmp = null来开始。

并且您正在渲染它,这意味着您将该对象附加到外部(函数)列表。 因此,没有任何数量的GC.Collect会实际收集它,因为它仍处于“播放中”状态。

将uielement源设置为null,这可能会消除附加的引用,为什么GC忽略它。

您可以将可写位图图像宽度X高度的大小限制为更小的尺寸,因为当您增加其大小时需要更多内存,因此您可以先限制其初始大小,然后另存为原始图块大小的jpeg

我在taskagent中将uielement转换为writeablebitmap时会遇到相同的exception。 刚试了这么多次。我找到了解决办法。

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

将uielement保存为流时,可以将质量更改为60。 它可以减少内存使用量。 你可以尝试一下。

试着把这部分:

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

最后条款;