.Net 中 Finalize 的成本
(1) 我读过很多关于 IDisposable 的问题,其中的答案建议不要使用 Finalize,除非由于涉及处理时间而确实需要。
我没有看到的是这笔费用是多少以及支付的频率。每毫秒?第二? 。
(2) 另外,在我看来,当并不总是知道某个对象是否可以被处置时,Finalize 很方便 例如,框架字体类。控件无法处理它,因为它不知道字体是否共享。字体通常是在设计时创建的,因此用户不知道如何处置它,因此最终确定会在没有剩余引用时最终删除它。这是正确的印象吗?
(1) I've read a lot of questions about IDisposable where the answers recommend not using Finalize unless you really need to because of the process time involved.
What I haven't seen is how much this cost is and how often it's paid. Every millisecond? second? hour, day etc.
(2) Also, it seems to me that Finalize is handy when its not always known if an object can be disposed. For instance, the framework font class. A control can't dispose of it because it doesn't know if the font is shared. The font is usually created at design time so the user won't know to dispose it, therefore finalize kicks in to finally get rid of it when there are no references left. Is that a correct impression?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
Finalize 的主要问题是它阻止对象被垃圾收集。相反,调用终结器,并“在下次运行时”收集对象。好吧,从技术上讲,IIRC 终结器在单独的线程中运行对象列表。无论如何,这不是一个“每毫秒”的问题,更多的是一个“需要运行多次 GC 来清除对象”的问题。
The main problem with finalize is that it blocks an object from being garbage collected. Instead, the finalizer is called, and the object collected "on the next run". Well, technically IIRC the finalizer runs a list of objects in a separate thread. Anyhow, this is not an "every ms" issue, more an "multiple GC runs needed to get rid of the objects.
Finalize 在概念上与 Dispose 不同。 Finalize只能释放非托管资源。 Dispose 可以释放托管和非托管资源。您应该酌情使用每一个。 (请注意,具有 Finalizer 的类应始终实现 IDisposable)。
必须显式调用 Dispose; Finalize只能由GC调用。
更新:请参阅我的博客文章 如何实现 IDisposable 和终结器:3 条简单规则。
Finalize is conceptually different than Dispose. Finalize can only free unmanaged resources. Dispose can free managed and unmanaged resources. You should use each as appropriate. (Note that a class with a Finalizer should always implement IDisposable).
Dispose must be called explicitly; Finalize can only be called by the GC.
Update: See my blog post on How to Implement IDisposable and Finalizers: 3 Easy Rules.
我有一篇关于 IDisposable 和 Finalizing 的博客文章 -但与性能无关。
I've got a blog post about IDisposable and Finalizing - not about the performance though.
我来回答你的第二个问题。
不,
Finalize
不应以这种方式使用。事实上,除了极少数的边缘情况外,如果类直接持有非托管资源,则只应重写Finalize
(或在 C# 中声明析构函数)。您所描述的问题是所有权问题之一。
IDisposable
类的所有者对其生命周期负责,并决定何时调用Dispose
。代码的其他部分可以自由地使用该类,但由于它们不能声明所有权,因此它们不应参与该类的生命周期管理。不幸的是,我不太熟悉
Font
类,也不熟悉它与引发您问题的特定场景的关系,但我可以做一个可能适用于您的一般性声明。如果您的代码没有直接创建实例(通过构造函数),那么您的代码不应被视为所有者。在这种情况下,您可以将处理责任留给其他人。I'll answer your second question.
No,
Finalize
should not be used in this manner. In fact, with the exception of only a very few fringe cases, you should only overrideFinalize
(or declare a destructor in C#) if the class directly holds unmanaged resources.The issue you described is one of ownership. The owner of an
IDisposable
class is responsible for its lifetime and the decision of when to callDispose
. Other parts of code are free to use that class, but since they cannot claim ownership they should not participate in the lifetime management of that class.Unfortunately I am not very familiar with the
Font
class nor how it might relate to the specific scenario that was the impetus for your question, but I can make a general statement that might apply to you. If your code did not create the instance (via the constructor) directly then your code should not be considered the owner. In that case you can assume the responsibility for disposal is left to something else.Finalize 作为双重检查非常有用。如果崩溃或某人的错误代码在超出范围之前没有处理您的对象,请保证其资源将在终结器中释放。
您可以通过调用 GC.SuppressFinalize(this) 在您的处理程序中进行一些花哨的操作,这将允许您编写一个在两种情况下都可以工作的方法,并保证代码能够很好地工作。
如果您正在编写一个框架来提醒人们应该处置您的对象,您甚至可以触发 MDA。
终结器的惩罚基本上是您最终将对象推入 2 级队列,这需要更长的时间来运行。如果您始终使用对象并且它们正在终结,这可能会导致 2 级集合的运行频率超过运行终结器线程所需的频率。
Finalize is extremely useful as a double check. If a crash or someone's bad code doesn't dispose your object before it goes out of scope, guarantee that its resources will be released in the finalizer.
You can do some fancy footwork in your disposer though by calling
GC.SuppressFinalize(this)
which will allow you to write a method which will work in both situations and give you a guarantee that the code will work nicely.You could even fire an MDA if you were writing a framework to remind people that they should dispose your object.
The penalty of the finalizer is basically that you end up pushing your object into the level 2 queue which takes longer to run. If you are consistently using objects and they are finalizing this could result in a level 2 collection running more often than neccessary just to run your finalizer threads.