C# 析构函数(又名:终结器)涉及的成本?
析构函数应该只释放对象所持有的非托管资源,并且不应该引用其他对象。 如果您只有托管引用,则不需要(也不应该)实现析构函数。 您只需要这样做来处理非托管资源。 由于使用析构函数会产生一些成本,因此您应该仅在消耗宝贵的非托管资源的方法上实现此功能。
本文没有更深入地讨论这一点,但是在 C# 中使用析构函数会涉及哪些成本?
注意:我了解 GC,并且知道析构函数不会在可靠的时间被调用,除此之外,还有其他什么吗?
The destructor should only release unmanaged resources that your object holds on to, and it should not reference other objects. If you have only managed references you do not need to (and should not) implement a destructor. You want this only for handling unmanaged resources. Because there is some cost to having a destructor, you ought to implement this only on methods that consume valuable, unmanaged resources.
The article doesn't go into this in more depth, but what sorts of costs are involved with using a destructor in C#?
Note: I know about the GC and the fact the destructor isn't called at reliable times, that all aside, is there anything else?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
任何具有终结器(与析构函数相比,我更喜欢该术语,以强调与 C++ 析构函数的区别)的对象都会添加到终结器队列中。 这是对对象的引用列表,这些对象具有在删除之前必须调用的终结器。
当对象准备进行垃圾回收时,GC 会发现它位于终结器队列中,并将引用移动到易碎 (f-reachable) 队列。 这是终结器后台线程依次调用每个对象的终结器方法所经过的列表。
一旦调用了对象的终结器,该对象就不再位于终结器队列中,因此它只是 GC 可以删除的常规托管对象。
这一切都意味着,如果一个对象有终结器,那么它在被删除之前至少会经历一次垃圾回收。 这通常意味着该对象将被移动到下一个堆生成,这实际上涉及将内存中的数据从一个堆移动到另一个堆。
Any object that has a finalizer (I prefer that term over destructor, to emphasize the difference from C++ destructors) is added to the finalizer queue. This is a list of references to objects that has a finalizer that has to be called before they are removed.
When the object is up for garbage collection, the GC will find that it's in the finalizer queue and move the reference to the freachable (f-reachable) queue. This is the list that the finalizer background thread goes through to call the finalizer method of each object in turn.
Once the finalizer of the object has been called, the object is no longer in the finalizer queue so it's just a regular managed object that the GC can remove.
This all means that if an object has a finalizer, it will survive at least one garbage collection before it can be removed. This usually means that the object will be moved to the next heap generation, which involves actually moving the data in memory from one heap to another.
我见过的关于如何所有这些作品都是由 Joe Duffy 完成的。 它包含的细节比您想象的还要多。
接下来,我整理了实用方法 每天都这样做 - 较少关注成本,但更多关注实施。
The most extensive discussion I've seen on how this all works was done by Joe Duffy. It has more detail than you might imagine.
Following that up, I put together a practical approach to doing this on a day to day - less about the cost but more about the implementation.
Guffa 和 JaredPar 很好地涵盖了细节,因此我将在终结器或析构函数上添加一些深奥的注释,不幸的是,C# 语言规范如此称呼它们。
需要记住的一件事是,由于终结器线程按顺序运行所有终结器,因此终结器中的死锁将阻止所有剩余(和未来)终结器运行。 由于这些实例在其终结器完成之前不会被收集,死锁的终结器也会导致内存泄漏。
Guffa and JaredPar cover the details pretty well, so I'll just add a somewhat esoteric note on finalizers or destructors as the C# language specification unfortunately calls them.
One thing to keep in mind is that since the finalizer thread runs all finalizers in sequence, a deadlock in a finalizer will prevent all remaining (and future) finalizers from running. Since these instances are not collected until their finalizers complete a deadlocked finalizer will also cause a memory leak.
Guffa 很好地总结了终结器成本的因素。 最近有一篇关于 Java 中终结器的成本的文章,其中也提供了一些见解。
.net 中的部分成本可以通过使用 GC.SuppressFinalize 从终结器队列中删除对象来避免。 我根据文章在 .net 中运行了一些快速测试并将其发布到 这里< /a> (尽管重点更多地集中在 Java 方面)。
下面是结果图 - 它实际上没有最好的标签;-)。 “debug=true/false”指的是空终结器与简单终结器:
“Suppress=true”指的是 Dipose 方法中是否调用了 GC.SuppressFinalize。
摘要
对于 .net,通过调用 GC.SuppressFinalize 从终结器队列中删除对象的成本是将该对象留在队列上的成本的一半。
Guffa has summed up the factors in the finalizer cost quite well. There was a recent article about the cost of finalizers in Java which also gives some insight.
Part of the cost in .net can be avoided by removing the object from the finalizer queue with GC.SuppressFinalize. I ran some quick tests in .net based on article and posted it here (although the focus is far more on the Java side).
Below is a graph of the results - it doesn't really have the best labels ;-). "Debug=true/false" refers to the empty vs simple finalizer:
"Suppress=true" refers to whether GC.SuppressFinalize was called in the Dipose method.
Summary
For .net, removing the object from the finalizer queue by calling GC.SuppressFinalize is half the cost of leaving the object on the queue.