假设我创建了一个位图
Bitmap bitmap = new Bitmap(320, 200);
当我把它写到某个流(在我的情况下,它是一个HttpResponseStream,由HttpListenerResponse给出),一切都很好:
bitmap.Save(stream, ImageFormat.Png);
我不需要bitmap.Dispose(),位图使用的资源会自动清理。直接将Png写入不可搜索的流的问题是,它可能会导致A generic error occurred in GDI+,当我在Azure上尝试我的Asp应用程序时,这发生在我身上。这就是我的代码现在的样子:
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms, ImageFormat.Png);
ms.WriteTo(stream);
}
现在除非我之后使用bitmap.Dispose(),否则会泄漏。
重新提出问题以获得更具体的答案: 为什么这个位图内存泄漏似乎取决于我将其保存到哪种类型的流?
更新 正如我在评论中被问及我是否确定这是泄密一样。在压力测试中反复调用上面的内容,我的w3wp进程将在我的机器开始交换之前使用gig和gig的内存,它将无法清理。
答案 0 :(得分:5)
Bitmap类使用非托管资源。这些资源与内存流类使用的资源无关。您可以将位图类包装在using语句中,以便在完成后处理位图实例。
错过了问题的后半部分。 “设置并忘记它”的一种方法是创建一个包装类,它暴露位图实例,但实现了一个处理位图实例的析构函数。这个析构函数意味着位图类在垃圾收集中被隐式处理。
作为最后一点:您实例化实现IDisposable的任何对象必须由您的代码处理。 Dipose永远不会被暗中调用。即使在你的第一个例子。仅仅因为您将数据保存到流中并不意味着内存已被释放。大多数情况下,在实例化它的同一代码段中对一个对象进行dipos是个好主意。这有助于通过提高代码透明度来更轻松地阅读代码。
答案 1 :(得分:2)
我认为问题在于GC会神奇地清理你的物体。但是,它可能永远不会这样做,而这就是我认为可能发生的事情:
位图使用非托管资源来保存位图数据,位图数据很大。因此,您将为每个位图分配一个微小的托管内存块和一大块非托管内存。
所以你留下你的位图,让GC在闲暇时收集。这适用于很多对象,因为很快就有足够的内存压力,GC会收集它们以重新使用内存。让GC查看托管堆并说“通过处理未使用的对象,我只能恢复64字节的内存。我不会打扰”。它没有看到数十亿字节的非托管资源,只看到其堆上的几个字节。
所以你需要自己跟踪和处理位图。
有时有可能你已经看到它为你清理了。这将是因为在存在情况下(例如当你处理其他对象,例如具有更大内存占用的流,或者只是因为它是星期二下午),它 选择处理未使用的内存块,并且然后你的位图最后处理。但你不能依赖来解决这个问题。
...漫步:
在过去的日子里,指针存在两个问题。
因此,在.net中,他们将“指针”重命名为“引用”,添加了GC并假装该问题不再存在。除了引用仍然可以为空,程序员仍然必须跟踪和管理他们的资源以避免泄漏 - 只是少一点点经常。我认为这是一件坏事 - 它使我们懒惰而且效率低而没有实际消除潜在的问题,所以它又回来并咬我们,我们最终写了大量的Dispose逻辑,我们过去常常只有一个简单的'删除'在我们的析构者中。
答案 2 :(得分:0)
您必须处置位图才能释放GDI +资源。就这么简单。这是为数不多的需要调用Dispose的时间之一。如果要缓存位图以减少磁盘访问,则克隆映像并使用克隆保存到流中。我强烈建议冲洗,关闭和处理流。完成后,将clone和stream变量设置为null。