使用析构函数/终结器昂贵吗?
我正忙着对非确定性破坏感到困惑。在回答另一个问题时,我得到了析构函数/终结器的建议(其中我认为在 C# 中也是一样的,即名为 ~classname()) 的函数非常昂贵并且不是必需的。但是看看这个示例使用了析构函数,从评论来看它可能很重要。有人对这一切如何组合在一起有一些建议吗?我应该从代码中删除析构函数吗?
再次感谢。
I'm busy getting confused about non-deterministic destruction. In an answer to another question I got the advice that destructors/finalisers (which I assume are the same thing in c#, i.e. the function called ~classname()) are expensive and not required. But looking at this example a destructor is used and from the comment sounds like it may be vital. Anyone got some advice about how this all fits together and should I remove the destructor from my code?
Thanks again.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
仅当您绝对必须在某个时刻运行一些清理操作(无论是否显式执行)时,才应包含终结器。在这种情况下,您应该始终有一种明确的方式及时执行清理,并且无论如何都应该抑制最终确定,以便“好”客户端不会看到任何性能损失。
如果您有非托管资源的直接句柄,通常只需要终结器 - 如果您仅引用另一个具有该资源句柄的类(例如
FileStream
)那么你应该把它留给另一个类来完成终结器。随着
SafeHandle
< 的出现/a> 在 .NET 2.0 中,值得编写自己的终结器的情况是 确实非常罕见。终结器的性能损失是它们使对象的生存时间超过了所需的时间:在第一个 GC 周期中,它们被认为有资格进行收集,它们被放入终结器队列中 - 并被提升到下一代就像任何其他在 GC 周期中幸存下来的对象一样。然后终结器将在另一个线程中运行(在某个时刻),只有然后它们才有资格真正被收集。因此,它们不是在第一个 gen1 集合中被收集,而是一直存活到下一个 gen2 集合,这可能会晚得多。
You should only include a finalizer if you absolutely have to run some cleanup at some point, whether it's performed explicitly or not. In such cases you should always have an explicit way of performing clean-up in a timely manner anyway, and that should suppress finalization anyway, so that "good" clients don't see any performance penalty.
You would normally only need a finalizer if you have a direct handle to unmanaged resources - if you only have a reference to another class which has a handle on the resource (e.g.
FileStream
) then you should leave it to the other class to have a finalizer.With the advent of
SafeHandle
in .NET 2.0, situations where it's worth writing your own finalizer are very rare indeed.The performance penalty of finalizers is that they make your objects live for longer than they need to: in the first GC cycle where they're otherwise considered eligible for collection, they get put on the finalizer queue - and bumped up to the next generation just like any other object which survives a GC cycle. The finalizer will then run in another thread (at some point) and only then will they be eligible to really be collected. So instead of (say) getting collected in the first gen1 collection, they live on past that until the next gen2 collection, which may be considerably later.
通常,实现析构函数在以下情况下很有用:当不能保证时,客户端代码将正确关闭所有资源(文件流、数据库连接等)。因此,如果客户端代码无法执行此操作,您将有代码将其关闭,这比仅将资源保持打开状态要好。
Usually, implementing a destructor is useful in on case: when it is not guaranteed, that client code will close all the resources(file streams, db connections and so on) properly. So, if client code will fail to do so, you will have code, which will close it, which is better than just left the resources open.
当您直接处理非托管资源时,您只需要完整的一次性模式。然后由您的调用代码来确保析构函数(几乎)从未使用过。
当处理托管资源(=非托管资源的间接所有权)时,析构函数是无用的:
每当 GC 收集 FileWrapper 对象时,就可以确定 fs 对象位于同一批次中。因此,对 fs.Dispose() 的调用是无用的,仅测试 FileStream.Dispose() 的正确(允许多次调用)行为。
这里唯一有用的析构函数是 FileStream 中的析构函数。
You only need the full Disposable pattern when you are dealing with an unmanaged resource directly. And then it is up to your calling code to make sure the destructor is (almost) never used.
When dealing with managed resources (= indirect ownership of unmanaged resources), the destructor is useless:
Whenever a FileWrapper object is being collected by the GC, it is certain that the fs object is in the same batch. So the call to fs.Dispose() is useless, only testing the correct (allowing multiple calls) behaviour of the FileStream.Dispose().
The only useful destructor here is the one in FileStream.