由于非托管内存处置延迟导致的内存不足

本文关键字:内存不足 延迟 于非托 内存 | 更新日期: 2023-09-27 17:53:00

我的应用程序因内存不足异常而崩溃,有时其他异常也可能由内存不足引起。

我用这段简单的代码重现了这个问题:
  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的常识相反。收集,但我怀疑这是一个场景,它实际上是有意义的。

有人能对此提供任何有见地的见解吗?有没有更好的方法来解决这个问题?

由于非托管内存处置延迟导致的内存不足

RenderTargetBitmap很可能有一个与之关联的本地资源。您已经获得了大量的托管内存(每分配一个X字节就会调用GC)—托管对象本身可能没有足够的内存使用,根本不会引起人们的兴趣。所以它一定是非托管部分——我希望它有一个DirectX纹理(或类似的东西)底层,它只会在finalizer被执行时被释放。

然而,由于没有足够的托管内存压力,GC实际上根本不会被调用,本机资源也不会被释放。

奇怪的是RenderTargetBitmap不是IDisposable。这意味着你不能尽快正确地处理本地资源。因此,它更像是WPF中的一个bug,而不是。net本身。

这只是一个假设。

要处理注释,GC绝对不会先等待方法退出。将RenderTargetBitmap替换为byte[],在不涉及本地资源的情况下可以正常工作。

EDIT:我终于在BCL源代码中找到了这个。要处理RenderTargetBitmap的本地资源,必须调用Clear。即使没有这样做,它最终也会被释放(本机资源在安全句柄上),但是如果您只分配和释放RenderTargetBitmap,那么在您甚至让GC运行之前很久就会耗尽纹理/本机内存。因此,要回答您现实生活中的问题,只需在不再需要时在位图上调用Clear,并且它应该不再占用内存。

2015年7月

:

看起来原来的bug已经被修复了——通过4.5.2的源代码,内存压力被正确应用,分配大量的RenderTargetBitmap现在应该会导致GC正确收集。但是仍然没有IDisposable实现