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(); }
看内存截图…
之前:
后:
你最后在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();
到最后条款;