Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/269.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当我没有将.Dispose()位图保存到MemoryStream时,为什么会出现内存泄漏?_C#_.net_Bitmap_Memorystream - Fatal编程技术网

C# 当我没有将.Dispose()位图保存到MemoryStream时,为什么会出现内存泄漏?

C# 当我没有将.Dispose()位图保存到MemoryStream时,为什么会出现内存泄漏?,c#,.net,bitmap,memorystream,C#,.net,Bitmap,Memorystream,假设我创建一个位图 Bitmap bitmap = new Bitmap(320, 200); 当我将其写入某个流时(在我的例子中,它是一个HttpResponseStream,由HttpListenerResponse给出),一切正常: bitmap.Save(stream, ImageFormat.Png); 我不需要位图.Dispose(),位图使用的资源将自动清理。但是,将Png直接写入不可查找的流的问题是,它可能会导致错误,这是我在Azure上尝试Asp应用程序时遇到的问题。这就是

假设我创建一个位图

Bitmap bitmap = new Bitmap(320, 200);
当我将其写入某个流时(在我的例子中,它是一个HttpResponseStream,由HttpListenerResponse给出),一切正常:

bitmap.Save(stream, ImageFormat.Png);
我不需要位图.Dispose(),位图使用的资源将自动清理。但是,将Png直接写入不可查找的流的问题是,它可能会导致错误,这是我在Azure上尝试Asp应用程序时遇到的问题。这就是我的代码现在的样子:

using (MemoryStream ms = new MemoryStream())
{
  bitmap.Save(ms, ImageFormat.Png);
  ms.WriteTo(stream);
}
现在,除非我稍后使用bitmap.Dispose(),否则这将泄漏

重新表述问题以获得更具体的答案: 为什么位图内存泄漏似乎取决于我将其保存到哪种类型的流?

更新:
正如我在评论中被问到的,我是否确定这是一个漏洞。在压力测试中反复调用上述函数,我的w3wp进程将占用越来越多的内存,直到我的机器开始交换,并且不会清理。

位图类使用非托管资源。这些资源与内存流类使用的资源无关。您只需将位图类包装在using语句中,即可在使用完位图实例后处理它

错过了你问题的后半部分。“设置并忘记它”的一种方法是创建一个包装类,该类公开位图实例,但实现一个处理位图实例的析构函数。这个析构函数意味着位图类在垃圾收集时被隐式处理


最后请注意:您实例化的任何实现IDisposable的对象都必须由您的代码处理。Dipose永远不会被隐式调用。即使在你的第一个例子中。仅仅因为您将数据保存到流中,并不意味着内存已被释放。大多数情况下,在实例化对象的同一段代码中对对象进行二次排序是一个好主意。这有助于提高代码透明度,从而使代码更易于阅读。

您必须处理位图才能释放GDI+资源。就这么简单。这是少数需要调用Dispose的情况之一。如果要缓存位图以减少磁盘访问,请克隆图像并使用克隆将其保存到流中。我强烈建议冲洗、关闭和处理水流。完成后,将clone和stream变量设置为null。

我认为问题在于假设GC会神奇地清理对象。然而,它可能永远不会这样做,我认为可能会发生以下情况:

位图使用非托管资源保存位图数据,并且位图数据很大。因此,您将为每个位图分配一个极小的托管内存块和一个巨大的非托管内存块

因此,您将位图放在周围,让GC在空闲时收集。这对很多对象都很有效,因为很快就会有足够的内存压力,GC会收集它们以重用内存。但是GC查看托管堆并说“通过处理未使用的对象,我只能恢复64字节的内存。我不想麻烦了”。它看不到千兆字节的非托管资源,只看到堆上的几个字节

因此,您需要自己跟踪和处理位图

有时你可能看到它为你清理干净了。这是因为在sme环境下(例如,当您处理其他对象时,例如内存占用较大的流,或者仅仅因为是星期二下午),它确实选择处理未使用的内存块,然后最终处理位图。但你不能指望这会发生

……漫谈:

在过去,指针有两个问题

  • 它们可能为null,导致代码崩溃
  • 您可能会忘记释放他们的内存/资源,从而导致泄漏

因此,在.net中,他们将“指针”重命名为“引用”,添加了GC,并假装问题不再存在。除了引用仍然可以为null之外,程序员仍然必须跟踪和管理他们的资源以避免泄漏——只是稍微少一点。我认为这是一件坏事——它让我们懒惰、效率低下,却没有真正消除潜在的问题,所以它又回来咬我们,我们最终编写了大量的Dispose逻辑,而我们过去在析构函数中只需要简单的“删除”即可。

你确定这是内存泄漏,而不仅仅是垃圾收集尚未清理它吗?你确定你有内存泄漏吗?随着.Net的发明,他们重新定义了内存泄漏的定义;)@詹姆斯;使用GDI+时,可能会出现系统内存泄漏,一旦AppDomain被释放,系统内存泄漏将被清除。如果我不调用Bitmap.Dispose(),位图将被清除或不会被清除,这仅取决于流I.Save()的类型。这就是我感到困惑的地方。顺便说一句,Bitmap.Dispose()实际上是隐式调用的,~Image()析构函数执行此操作。检查源代码。为什么在保存到MemoryStream以外的其他文件时没有泄漏?我不打电话到那里,而且效果很好。添加我在另一条评论中所说的内容:~Image()析构函数确实隐式调用了Bitmap.Dispose()。克隆给我带来了什么好处?我不知道克隆做什么,但听起来它会消耗更多的内存。(MSDN docs only状态:创建此图像的精确副本。)。顺便说一句,我的案子不涉及磁盘。这些是生成的图像,缓存以平衡cpu时间和内存。@EugeneBeresovksy;是的,我知道Dispose是隐式调用的。我还知道.NET正确地初始化和终止GDI库。我告诉您的是基于十年的.NET经验,您需要调用Dispose,并且需要将保存图像的变量设置为null。@EugeneBeresovksy;克隆可确保缓存映像的安全并避免