由于非托管内存处理的延迟而导致内存不足?

时间:2014-03-25 15:29:01

标签: c# .net wpf

我的应用程序因内存不足异常而崩溃,有时可能还会因内存不足而导致其他异常。

我用这个简单的代码重现了这个问题:

  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的常识,但我怀疑这是一个确实有意义的场景。

有人能提供任何明智的见解吗?有没有更好的方法来解决问题?

1 个答案:

答案 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实现。