这是我知道的一个老问题,但我一直在对涉及 GDI 对象的资源泄漏进行一些研究,并且接受的答案中几乎每个陈述都是错误的。为了让后来的读者像我一样通过搜索找到这个问题的准确性:
不需要调用 Dispose。
这种说法具有误导性。尽管从技术上讲,您可以不打电话而逃脱惩罚Dispose
,不丢弃画笔或钢笔是非常糟糕的做法你拥有的一旦你完成了它。
原因是:进程中可以突出显示的 GDI 对象的数量存在硬性限制(默认设置为一万,但可以通过注册表破解来增加)——注意,不是应用程序域——并且垃圾收集器可能无法比资源泄漏的速度更快地完成资源的完成。托管内存分配产生收集压力但没有对应的最终确定压力.
垃圾收集的目的就是消除这些要求。
它不是。垃圾收集的目的是模拟具有任意多内存的环境。
主要目的之一IDisposable
就是允许一个类在资源有限的环境中清理非托管资源。
这是对的。
如果您不致电Dispose()
方法,一旦对象在垃圾收集期间完成并释放,该类的非托管资源将被清除。
这有时是正确的;对于 GDI 对象来说这是正确的。对于持有非托管资源的类来说,实现终结语义作为后盾是一种很好的做法;为遵循已接受答案中给出的不良建议的人提供额外的保护。你不应该依赖终结器来避免错误;你应该处置你的资源。
您应该只依靠终结器来处理疯狂的异常情况。例如考虑:
Brush myBrush = MakeMeABrush();
DoSomethingWithBrush(myBrush);
myBrush.Dispose();
假设抛出了线程中止异常after刷子的配置不过before分配给 myBrush。没有组合try-catch-finally
将使您能够通过以下方式清理刷子Dispose
;你必须依赖终结器。这就是终结器的用途:完全疯狂你的情况cannot处置你自己。这不是马虎的借口。
如果您“必须”调用 dispose 并且您不知道画笔实例是“系统”还是“普通”画笔,那么您将不得不使用 try...catch 块。
尽管这在技术上也是正确的,但它完全没有抓住要点。如果您不知道自己是否拥有画笔,那么您的程序设计中存在错误。不要用try-catch
堵塞!修复错误!
这种情况对于所有显式管理的资源都很常见:提供资源的实体和消耗资源的实体负责清楚地记录两者中哪一个拥有清理资源。如果您不知道您是否拥有所提供的刷子,那么有人没有完成他们应该做的任务,即防止这种情况发生.
如果您决定的合同是提供资源的实体负责稍后清理它您的消费者根本不应该丢弃刷子,因为这违反了合同;如果需要的话,生产者会清理它。
如果您决定的合同是生产者和消费者都将释放资源,那么消费者必须调用Clone
on every刷子传入以确保他们拥有安全可处置的资源,并且生产者也继续拥有有效的资源。
如果您决定的合同很可能是消耗资源的实体负责稍后清理它提供者必须始终为您提供可以安全处置的刷子,并且需要not自行处置资源。自从provider知道他们是自己制作刷子还是从系统获取刷子,提供者必须调用Clone()
在系统刷子上获取可以安全处理的刷子,然后通过that给消费者。
但“没有人清理它,我们希望得到最好的结果”的合同是一份相当糟糕的合同。
不需要调用Dispose()
on SystemBrushes
and SystemPens
因为 GDI+ 库会处理这些资源。
这个解释只是一个解释,实际上并没有解释任何事情。处理这些刷子之一是违法的原因是画笔的生命周期等于appdomain的生命周期.
类的备注部分将注明是否需要调用 Dispose 方法。
这种说法是错误的。您应该假设始终有必要Dispose
an IDisposable
资源,除非您有充分的理由不相信。文档中没有一行并不表明不需要进行处理。
以积极的方式结束:
如果我有一个图形密集型应用程序或类,那么我将缓存我需要的笔和画笔的实例。我在应用程序或类的整个生命周期中使用它们。
这是一个很好的做法。如果创建的画笔和笔的数量相对较少,并且一遍又一遍地使用相同的画笔和笔,那么永久缓存它们是很有意义的。由于它们的生命周期已到程序结束,因此无需处置它们。在这种情况下,我们不会处理垃圾,因为这不是垃圾;这很有用。 GDI 对象是not缓存当然仍然应该被处置。再次强调,追求缓存策略并不是采取不良做法的借口。