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