【发布时间】:2016-05-08 13:28:41
【问题描述】:
我想创建一个获取图像并返回一些派生对象的管道。
我正在使用一系列位图,并为每个位图执行任务(即异步)。所以它看起来很简单。但是,我发现内存消耗真的很高。为了说明问题,我创建了这个可以运行的测试。
请看一下内存,因为它会占用多达 400 MB 的 RAM。
我可以做些什么来避免占用这么多内存?这里发生了什么?
[Fact]
public async Task BitmapPipelineTest()
{
var bitmaps = Enumerable.Range(0, 100).Select(_ => new WriteableBitmap(800, 600, 96, 96, PixelFormats.Bgr24, new BitmapPalette(new List<Color>() { new Color() })));
var bitmapsObs = bitmaps.ToObservable();
var processed = bitmapsObs.SelectMany(bitmap => DoSomethingAsync(bitmap));
processed.Subscribe();
await Task.Delay(20000);
}
private async Task<object> DoSomethingAsync(BitmapSource bitmap)
{
await Task.Delay(1000);
return new object();
}
【问题讨论】:
-
如果你搜索“WriteableBitmap”和“内存泄漏”,有很多发帖者遇到同样的问题,并且缺乏好的解决方案。如果您不需要使用 WriteableBitmap(即,如果您不做 UI 工作),那么我将使用 Bitmap 类,并修改您的代码以使用 SelectMany 中的 using 语句创建/处理 Bitmap跨度>
-
@Andrew - 这可能与您描述的问题有关。我认为这取决于 OP 使用的 .NET 版本。看起来内存问题在 4.0 中得到了解决。在我使用 .NET 4.5 进行的测试中,内存确实会迅速失控,但它最终会被垃圾回收,所以我认为没有内存泄漏。
-
@JasonBoyd - 有趣的是,使用 4.5,我能够破坏最大内存分配 (2Gb) 并使进程崩溃。在位图超出我修改的代码的范围后调用 GC.Collect() 也没有阻止它(尽管我永远不会考虑将这样的 GC 调用投入生产)
-
@Andrew - 是的,由于 cmets 中的空间有限,当我说“它最终确实会收集垃圾”时,我掩饰了很多。事实上,如果我让它不加检查地运行,我的应用程序也会崩溃。但是,我可以通过直接调用
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();来释放内存,这向我表明这不是内存泄漏,而是 GC 计划的问题(这是我在回答中提到的)。我还能够通过在按钮单击事件处理程序后面以 200 个批次运行 GC 来触发它们。
标签: c# .net system.reactive reactive-programming