我的应用程序因内存不足异常而崩溃,有时可能还会因内存不足而导致其他异常。
我用这个简单的代码重现了这个问题:
for (int i = 0; i < 100000; i++)
var bmp = new RenderTargetBitmap(256, 256, 96, 96, PixelFormats.Default);
理论上,这段代码不会崩溃,因为位图应该自动进行垃圾回收,但在32位模式下运行时会一直崩溃。
问题可以修复如下:
for (int i = 0; i < 100000; i++)
{
var bmp = new RenderTargetBitmap(256, 256, 96, 96, PixelFormats.Default);
if (i % 500 == 0)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
当然,这个解决方案违背了你不应该明确地调用GC.Collect的常识,但我怀疑这是一个确实有意义的场景。
有人能提供任何明智的见解吗?有没有更好的方法来解决问题?
答案 0 :(得分:12)
RenderTargetBitmap
最有可能拥有与之关联的本机资源。您拥有足够的托管内存(每次分配 X 字节时都会调用GC) - 托管对象可能根本没有足够的内存使用而感兴趣。因此它必须是非托管部分 - 我希望它具有基础的DirectX纹理(或类似的东西),只有在执行终结器时才会释放。
但是,由于管理内存压力不够,GC实际上根本没有被调用,原生资源也不会被释放。
奇怪的是RenderTargetBitmap
不是IDisposable
。这意味着您无法尽快妥善处理本机资源。所以,它更像是WPF中的一个错误而不是.NET本身。
但这只是一个假设。
要发表评论,GC肯定不会等待该方法先退出。将RenderTargetBitmap
替换为byte[]
可以在原始资源无法使用时正常显示。
编辑:我终于设法在BCL源代码中找到了这个。要处置RenderTargetBitmap
的原生资源,您必须致电Clear
。即使没有它,它也将被释放 (本机资源处于安全的处理状态),但如果你只是分配和解除分配RenderTargetBitmap
,你就会去在你运行GC之前很久就会耗尽纹理/本机内存。所以要回答你的现实生活中的问题,只需在位图上调用Clear
就不再需要它了,它不应该再占用内存了。
2015年7月:
原来的错误似乎已经修复了 - 通过4.5.2来源,正确应用了内存压力,分配大量RenderTargetBitmap
s现在应该使GC正确收集。但是仍然没有IDisposable
实现。