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

Met*_*per 15 .net c# wpf

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

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

  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肯定不会等待方法首先退出.替换RenderTargetBitmapbyte[]显示此功能在不涉及本机资源时正常工作.

编辑:我终于设法在BCL源代码中找到了这个.要处理本机资源RenderTargetBitmap,你必须打电话Clear.即使没有它,它最终也会被释放(原生资源处于安全处理状态),但是如果你只是分配和解除分配RenderTargetBitmap,那么在你运行GC之前很久就会耗尽纹理/本机内存.因此,要回答你的现实问题,只需Clear在不再需要时调用位图,它就不应该再占用内存了.

2015年7月:

原来的bug似乎已经修复了 - 通过4.5.2源查看,内存压力正确应用,并且分配大量的RenderTargetBitmaps现在应该使GC正确收集.但是仍然没有IDisposable实施.