避免处置系统定义的 Pen 和 Brush 实例
我知道最好的做法是在 Pen 的实例上调用 Dispose() 和 画笔,除非它们'已设置为系统预定义值(例如 System.Drawing .画笔,System.Drawing.Pens 或 System.Drawing.SystemBrushes)
尝试处置系统定义的资源会导致抛出异常。
如何检测(除了将 Dispose() 调用包装在 try/catch 中之外)这些资源之一是否引用系统定义的值或用户定义的值似乎并不明显。
I understand it is best practise to call Dispose() on instances of Pen and Brush, except if they've been set to the system-predefined values (eg. System.Drawing.Brushes, System.Drawing.Pens or System.Drawing.SystemBrushes)
Trying to dispose a system-defined resource results in an exception being thrown.
It doesn't appear to be obvious how you can detect (apart from wrapping the Dispose() call in a try/catch) whether one of these resources is referencing a system-defined or user-defined value.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
这是我知道的一个老问题,但我一直在对涉及 GDI 对象的资源泄漏进行一些研究,并且接受的答案中几乎每个陈述都是错误的。为了让后来的读者像我一样通过搜索找到这个问题的准确性:
这种说法具有误导性。虽然从技术上讲,您可以不调用
Dispose
,但使用完您拥有的画笔或笔后不将其丢弃是一种非常糟糕的做法。原因是:进程中可以突出显示的 GDI 对象的数量存在硬性限制(默认设置为一万,但可以通过注册表破解来增加)——注意,不是应用程序域——并且垃圾收集器可能无法比资源泄漏的速度更快地完成资源的完成。托管内存分配会产生收集压力,但没有相应的最终化压力。
它不是。垃圾收集的目的是模拟具有任意多内存的环境。
这是正确的。
这有时是正确的;对于 GDI 对象来说这是正确的。对于持有非托管资源的类来说,实现终结语义作为后盾是一种很好的做法;为遵循已接受答案中给出的不良建议的人提供额外的保护。 您不应该依赖终结器来避免错误;你应该处置你的资源。
你应该只依靠终结器来处理疯狂的异常情况。例如,
假设在分配画笔之后但在分配给 myBrush 之前抛出线程中止异常。
try-catch-finally
的任何组合都无法让您通过Dispose
清理该画笔;你必须依赖终结器。这就是终结器的作用:在完全疯狂的情况下,你无法处置自己。 这不是马虎的借口。尽管这在技术上也是正确的,但它完全没有抓住重点。 如果您不知道自己是否拥有画笔,那么您的程序设计中存在错误。不要用
try-catch
块来掩盖错误!修复错误!这种情况对于所有显式管理的资源来说都是常见的:提供资源的实体和消耗资源的实体负责清楚地记录两者中哪一个拥有清理资源。如果您不知道您是否拥有所提供的刷子,那么某人就没有完成他们本应负责的任务,即防止这种情况发生产生。
如果您决定的合同是提供资源的实体负责稍后清理它,那么您的消费者根本不应该处置刷子,因为这违反了合同;如果需要的话,生产者会清理它。
如果您决定的合同是生产者和消费者都将释放资源,那么消费者必须对传入的每个画笔调用“Clone”,以确保它们已安全可处置的资源,并且生产者也继续拥有有效的资源。
如果您决定的合同很可能是消耗资源的实体负责稍后清理资源,那么提供商需要始终为您提供可以安全处置的刷子,并且被要求不处置资源本身。由于提供者知道他们是自己制作画笔还是从系统获取画笔,因此提供者必须对系统画笔调用
Clone()
以获得可以安全处置的画笔,然后将那个传递给消费者。但“没有人清理它,我们希望得到最好的结果”的合同是一份相当糟糕的合同。
这个解释只是一个解释,实际上并没有解释任何事情。处置这些画笔之一是非法的,因为画笔的生命周期等于应用程序域的生命周期。
这种说法是错误的。您应该假设始终需要
Dispose
IDisposable
资源,除非您有充分的理由不这么认为。文档中没有一行并不表明不需要进行处理。以积极的方式结束:
这是一个很好的做法。如果创建的画笔和笔的数量相对较少,并且一遍又一遍地使用相同的画笔和笔,那么永久缓存它们是很有意义的。由于它们的生命周期已到程序结束,因此无需处置它们。在这种情况下,我们不会处理垃圾,因为它不是垃圾;这很有用。当然,未缓存的 GDI 对象仍应被释放。再次强调,追求缓存策略并不是采取不良做法的借口。
This is an old question I know, but I've been doing some research into resource leaks involving GDI objects, and almost every statement in the accepted answer is false. In the interests of accuracy for later readers who find this question via a search, as I did:
This statement is misleading. Though technically you can get away with not calling
Dispose
, it is an extremely poor practice to not dispose of a brush or pen that you own once you're done with it.The reason is: there is a hard limit (set to ten thousand by default, though that can be increased via registry hacks) of the number of GDI objects that can be outstanding in a process -- not application domain, note -- and the garbage collector might not get around to finalizing resources faster than they are being leaked. Managed memory allocations produce collection pressure but there is no corresponding finalization pressure.
It is not. The purpose of garbage collection is to emulate an environment with arbitrarily much memory.
This is correct.
This is sometimes correct; it is correct for GDI objects. It is a good practice for classes which hold on to unmanaged resources to implement finalization semantics as a backstop; an extra layer of protection for people who follow the bad advice given in the accepted answer. You should not rely on finalizers to save you from your mistakes; you should dispose of your resources.
You should only rely on a finalizer to deal with crazy exceptional situations. Consider for example:
Suppose a thread abort exception is thrown after the allocation of the brush but before the assignment to myBrush. No combination of
try-catch-finally
will enable you to clean up that brush viaDispose
; you'll have to rely upon the finalizer. That's what the finalizer is for: the completely crazy situations where you cannot dispose yourself. It is not an excuse to be sloppy.Though this is again, technically correct, it misses the point completely. If you are in a situation where you do not know whether or not you own the brush then you have a bug in your program design. Do not paper over your bugs with a
try-catch
block! Fix the bug!This situation is common to all explicitly-managed resources: the entity which provides the resource and the entity which consumes the resource are responsible for clearly documenting which of the two owns cleaning up the resource. If you don't know whether you own the brush that you've been given or not then someone isn't doing a task they were responsible to do, namely, preventing that situation from ever arising.
If the contract you decide upon is that the entity which provides the resource is responsible for cleaning it up later then your consumer shouldn't be disposing of the brush at all, because that is breaking the contract; the producer will clean that up if it needs to.
If the contract you decide upon is that both the producer and consumer are going to free the resource, then the consumer must call
Clone
on every brush passed in to ensure that they have a safely disposable resource, and that the producer continues to own a valid resource as well.If, most likely, the contract you decide upon is that the entity which is consuming the resource is responsible for cleaning it up later then the provider is required to always hand you a brush that you can safely dispose, and is required to not dispose the resource itself. Since the provider knows whether they made the brush themselves or got it from the system, the provider must call
Clone()
on system brushes to obtain a brush that can be safely disposed, and then pass that to the consumer.But the contract "no one cleans it up and we hope for the best" is a pretty poor contract.
This explanation is an explanation that doesn't actually explain anything. The reason why it is illegal to dispose of one of these brushes is because the lifetime of the brush is equal to the lifetime of the appdomain.
This statement is false. You should make the assumption that it is always necessary to
Dispose
anIDisposable
resource unless you have good reason to believe otherwise. The absence of a line in the documentation is not evidence that disposing is unnecessary.To end on a positive note:
This is a good practice. If the number of brushes and pens created is relatively small and the same ones are being used over and over again, then it makes good sense to cache them permanently. Since their lifetimes are to the end of the program there is no need to dispose them. In this case we are not disposing the garbage because it's not garbage; it's useful. GDI objects that are not cached should of course still be disposed. Again, pursuing a caching strategy is not an excuse to engage in poor practices.
当您完成创建的 Graphics 对象后,必须通过调用其 Dispose 方法来处理它。 (这条规则对于许多不同的 GDI+ 对象都是正确的。)不要保留它以备不时之需,因为它以后将不再有效。当你用完它后,你必须、必须、必须把它处理掉。如果不这样做,可能会导致图像损坏、内存使用问题,或更糟糕的是,导致国际武装冲突。因此,请正确处理所有 Graphics 对象。
如果您在事件中创建 Graphics 对象,则确实需要在退出该事件处理程序之前处理它。无法保证 Graphics 对象在以后的事件中仍然有效。此外,随时可以轻松地重新创建另一个 Graphics 对象。
从这里得到
http://msdn.microsoft.com/en-us /library/orm-9780596518431-01-18.aspx
When you are finished with a Graphics object that you create, you must dispose of it by calling its Dispose method. (This rule is true for many different GDI+ objects.) Don't keep it around for a rainy day because it won't be valid later. You must, must, must dispose of it when you are finished with it. If you don't, it could result in image corruption, memory usage issues, or worse yet, international armed conflict. So, please dispose of all Graphics objects properly.
If you create a Graphics object within an event, you really need to dispose of it before exiting that event handler. There is no guarantee that the Graphics object will still be valid in a later event. Besides, it's easy to re-create another Graphics object at any time.
Got from here
http://msdn.microsoft.com/en-us/library/orm-9780596518431-01-18.aspx
首先,您始终应该尽可能丢弃刷子,而不是将其交给垃圾收集器。虽然 GDI 最终会抽出时间来处理这些事情(假设库被正确关闭),但不知道什么时候会发生。事实上,我的理解是刷柄会长期存在。在长时间运行的应用程序过程中,您会看到事实上的内存泄漏。当然,在一个不会运行很长时间的小型应用程序中,或者在一个很少创建画笔的应用程序中,您可以让系统处理它,但这让我觉得很草率。
作为一般规则,每当我创建画笔时,我都会在 using 语句中进行操作。这会自动调用画笔的处理,而不必担心它。作为额外的好处,由于您在语句内创建画笔,因此您知道它不是预定义的画笔。任何时候您创建和使用非预定义的画笔时,将块包装在 using 中,您根本不必担心它。事实上,您甚至不需要显式调用 Dispose,因为即使出现异常,块也会这样做。
First of all, you always should Dispose of brushes when you can and not leave it up to the garbage collector. While GDI will eventually get around to taking care of that stuff (assuming the library gets shut down properly), there's no telling when that may be. In fact, my understanding is that brush handles stick around for the long-term. Over the course of a long-running application, you're looking at a de facto memory leak. Sure, in a small application that won't run for long, or in one that only rarely creates brushes, you can let the system handle it, but that strikes me as sloppy.
As a general rule, whenever I create a brush I do so in a using statement. That automatically calls dispose on the brush without having to worry about it. As an added bonus, since you create the brush inside the statement you know that it's not a predefined brush. Any time you create and use a non-predefined brush, wrap the block in a using and you don't have to worry about it at all. In fact, you don't even need to explicitly call Dispose, since the block will do so even in the case of an exception.
简短的回答是,..如果您创建它,要么委派清理责任,要么自己清理对象。通过让垃圾收集器挂起,您可能会造成 GDI 资源“泄漏”。它们最终可能会被清理干净,但挂在那里并没有任何好处。即,如果您不对打开的文件调用“close”或 Dispose,则该文件将保持锁定状态,直到 GC“处理它”
Short answer is,.. if you create it, either delegate responsibility to clean up or clean the objects up yourselves. You can create GDI resource "leaks" by letting things hang in the garbage collector. They may eventually be cleaned up, but they are not doing any good hanging there. i.e. if you don't call "close" or Dispose on opened files, the file's remain locked until the GC "gets around to it"
我可能是错的,但我认为您可以假设预定义画笔和笔的生命周期(和处理)不是您的应用程序的责任,并将由系统处理。
简而言之:不要对预定义的内容调用 Dispose。 :)
I could be wrong but I think you can assume that the lifetime (and the disposal) of the predefined brushes and pens is not your app's responsibility and will be handled by the system.
In short: don't call Dispose on the predefined stuff. :)
唯一要记住的是不要使用系统笔/画笔作为方法的参数。
Only thing come in mind is to have a practice do not use System pens/brushes as parameters to methods.
为了完整起见,使用 Reflector 我看到 System.Drawing.Pen 和 System.Drawing.SolidBrush 类有一个名为“immutable”的私有字段。
当对象引用系统定义的资源时,该字段似乎设置为 true,因此您可以使用反射仔细检查该字段的值,以决定是否对其调用 Dispose()。
Just for completeness, using Reflector I see that the System.Drawing.Pen and System.Drawing.SolidBrush classes have a private field named 'immutable'.
This field appears to be set to true when the object refers to a system-defined resource, so you could use reflection to carefully check the value of this field to decide whether to call Dispose() on it.
不需要调用
Dispose
。垃圾收集的目的就是消除这些要求。IDisposable
的主要目的之一是允许类在资源有限的环境中清理非托管资源。如果不调用 dispose 方法,则一旦对象在垃圾收集期间完成并释放,该类的非托管资源将被清除。如果您“必须”调用 dispose 并且您不知道画笔实例是“系统”还是“普通”画笔,那么您将不得不使用 try...catch 块。
SystemFonts
和SystemIcons
。类的备注部分将记录是否需要调用
Dispose
方法。 如果备注部分建议调用 Dispose 方法,那么我会这样做。一般来说,我不会对笔和画笔调用 dispose。如果我有一个图形密集型应用程序或类,那么我将缓存我需要的笔和画笔的实例。我在应用程序或类的整个生命周期中使用它们。如果我不这样做,那么尝试多次且频繁地创建和处理所有这些对象时,图形绘制性能将会受到影响。 (嗯……现在我想了一下,性能可能就是为什么我们不能处理SystemBrushes和SystemPens,但却可以处理SystemFonts和SystemIcons。甚至框架也缓存了SystemBrushes和SystemPens。)
There is no requirement to call
Dispose
. The purpose of garbage collection is to eliminate these kinds of requirements.One of the main purposes of
IDisposable
is to allow a class to clean up unmanaged resources in resource-limited environments. If you do not call the dispose method, the unmanaged resouces of the class will be cleaned up once the object is finialized and disposed during garbage collection.If you "must" call dispose and you do not know if the brush instance is a "system-" or a "normal-" brush then you will have to use a try...catch block.
SystemBrushes
andSystemPens
because the GDI+ Library will take care of these resources.SystemFonts
andSystemIcons
.The Remarks section of the class will make note of if there is a requirement to call the
Dispose
method. If the Remarks section recommends to call the Dispose method, then I will do it.In general, I do not call dispose on pens and brushes. If I have a graphic-intensive application or class then I will cache instances of the pens and brushes I need. I use them throughout the life of the application or class. If I didn't do this then graphics painting performance will suffer trying to create and dispose all those ojects so many times and so frequently. (Hm...now that I think about it, performance is probably why we cannot dispose of SystemBrushes and SystemPens, yet can dispose of SystemFonts and SystemIcons. Even the framework caches SystemBrushes and SystemPens.)