【发布时间】:2013-01-20 13:48:02
【问题描述】:
我正在使用下面的代码来创建一个基于 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 内存。
再运行一些此方法(在调试器中重新设置为运行方法):
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;
}
启动应用后:ApplicationCurrentMemoryUsage: "11 681 792 Bytes"
1 次迭代 - ApplicationCurrentMemoryUsage:“28 090 368 字节”
5 次迭代 - ApplicationCurrentMemoryUsage:“77 111 296 字节”
20 次迭代 - ApplicationCurrentMemoryUsage:“260 378 624 字节”
23 次迭代后:内存不足异常。 ln.: var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);
仅通过注释掉“Fill = solidColorBrush”行,CreateImage() 方法被调用了 100 次,没有任何问题 - 在第 100 次迭代之后,内存使用量约为“16 064 512 字节”。
看来问题出在画笔上!! 当用于填充 UI 元素,然后此 UI 元素在可写位图上呈现时,位图永远不会被垃圾收集。
在我看来,这当然没有意义。 刷子超出范围,所以它也应该被垃圾收集! (使用后将画笔设置为null并没有改变任何东西)
我的许多 UI 元素都使用画笔进行填充,因此我不能简单地删除画笔的使用。 您如何看待这个问题?
【问题讨论】:
-
您应该注意,通常没有理由直接调用 GC.Collect()。它可能有害、冻结您的应用程序并影响性能。
-
确实,这更像是某种“拼命尝试”,实际上它什么也不做,也不影响内存消耗。
-
想知道如果完成画布会发生什么?
-
画布仅用于在 WriteableBitmap 上渲染,在此方法后不再使用,也不再显示在 UI 中,因为这是一个后台任务。但你是对的,将其设置为 null 并不是“好的做法”,但它对行为完全没有影响。
-
您能否通过注释掉代码行然后检查泄漏来进一步缩小泄漏的原因。首先,只是渲染和无效但不写入存储?漏水还在吗?然后,只需将 jpeg 写入隔离存储,无需先渲染和失效。我猜一些系统进程正在持有对 WriteableBitmap 的前缓冲区或后缓冲区的引用......
标签: c# windows-phone writeablebitmap live-tile