垃圾收集器是否隐式使用析构函数方法,开发人员是否使用显式处置对象的处置方法?
我可以看到已经有很多关于处置与析构函数方法的线程,但我只是想确保在继续之前我正确理解它们。
当对象不再被引用(即不再需要)时,垃圾收集器是否隐式使用析构函数方法,以及我们开发人员使用的处置方法来显式处置垃圾收集器可能无法处理的对象?
另外 - 我现在正在阅读所有这些内容,似乎这些方法都是其中一种情况。例如,给出以下代码:
class DansClass : IDisposable
{
public void Dispose()
{
GC.SuppressFinalize(this);
Console.WriteLine("Disposing...");
}
-DansClass()
{
Console.WriteLine("Destructing...");
}
}
输出将是:
Destructing...
可以理解,因为我们已经抑制了 Finalize(析构函数),因此我们只能看到 Dispose 输出。
但是,如果我注释掉 SuppressFinalize() 方法,则输出为:
Dispose...
为什么析构函数也没有被调用?
I can see there's already a lot of threads regarding dispose vs. destructor methods but I just want to make sure that I'm understanding them correctly before I move on.
Are destructor methods used implicitly by the garbage collector for when objects are no longer referenced (i.e. no longer needed) and dispose methods used by us developers to explicitly dispose of objects that may not be handled by the garbage collector?
Also - I'm reading into all this now and it seems that it's a case of one or the other with these methods. So for example given the following code:
class DansClass : IDisposable
{
public void Dispose()
{
GC.SuppressFinalize(this);
Console.WriteLine("Disposing...");
}
-DansClass()
{
Console.WriteLine("Destructing...");
}
}
The output will be:
Destructing...
Understandable, as we've suppressed the finalize (destructor) so we only see the Dispose output.
But if I comment out the SuppressFinalize() method the output is:
Disposing...
Why isn't the destructor called as well?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
那么您看到的行为是因为 终结器 (您所称为析构函数,在 .Net 中也称为终结器),由垃圾收集器排队在后台运行。
最终它将被调用(尽管在某些情况下可能不会)。 您可以强制执行它们,但是要了解什么是这样的:
只有当你有外部资源时才需要实现该模式,或者封装包含外部资源的成员。仅当您拥有外部非托管资源时才应实现终结器。
正确实现
IDisposable
模式需要:Dispose
清理非托管和托管资源Dispose
必须可多次调用Dispose
实施Close
)在功能上应该等同于Dispose
(如果两者都存在)Well the behavior you're seeing is because Finalizers (what you're refering to as a Destructor is also known as a Finalizer in .Net) are queued up to run in the background by the Garbage Collector.
Eventually it will be called (although in some instances it may not). You can force their execution, however, to understand what is going on:
You only need to implement the pattern when you have external resources, or encapsulate members which contain external resources. Finalizers should only be implemented when you have external, unmanaged resources.
Proper implementation of the
IDisposable
pattern requires:Dispose
cleans up both unmanaged and managed resourcesDispose
must be callable more than onceDispose
implementationClose
, should be functionally equivalent toDispose
if both existDispose 方法、终结器和 C# 的具有讽刺意义的析构函数之所以存在,是因为许多对象要求其他实体代表它们执行操作,直到另行通知(通常授予对文件、内存区域、通信流、硬件设备等内容的独占使用权)。 GDI手柄等);如果发出此类请求的对象消失,而其他实体不知道不再需要它们的服务,则代表被遗弃对象保留的任何内容都将仍然无法访问。
IDisposable.Dispose 方法提供了一种很好的一致方式来告诉对象,它将不再被要求做任何需要其他实体的帮助的事情,并且任何其他实体正在其上做任何事情。应告知代表停止这样做。如果正确调用 IDisposable.Dispose,对象可以最大限度地减少外部实体必须代表它们执行操作的程度。
不幸的是,由于各种原因(大多数是很容易避免的;有些则不然),对象有时会在没有调用 IDisposable.Dispose 的情况下被放弃。这将导致外部实体不得不徒劳地继续代表被遗弃的对象采取行动,至少在一段时间内如此。为了避免外部实体永远代表外部实体行事,系统可以通知对象它们已被放弃,从而使它们有机会将此事实通知外部实体。每当创建一个其类重写
Object.Finalize
的对象时,它将被放置在一个特殊的对象列表中,如果它们被放弃,则需要通知该对象。存在于该列表中本身并不足以使对象被视为“存活”,但在垃圾收集器从内存中删除死对象之前,它会检查它们是否在“销毁前通知”列表中。如果/当它们被放弃时,列表上的所有失效对象将从请求通知的对象列表移至需要通知它们已被放弃的对象列表。放置在第二个列表上将导致死亡对象以及它们所引用的任何对象再次被视为“活动”,至少在执行通知之前是如此。然而,当它们被移动到第二个列表时,它们就会从第一个列表中删除,这样如果以后发现它们再次死亡,它们就会从内存中清除,恕不另行通知。垃圾回收完成后,如果需要通知放弃的对象列表中存在任何对象,系统将调用每个此类对象的
Object.Finalize
方法。通常,一旦对此类对象调用了Object.Finalize
,就不会再有任何对它的根引用,并且它将在下一次垃圾回收时消失。然而,物体有可能复活。在 vb.net 以及据我所知的大多数 .net 语言中,只需以通常的方式声明一个覆盖即可覆盖
Object.Finalize
。无论出于何种原因,C# 的创建者决定禁止这样做。相反,在 C# 中,必须使用一种语言结构(讽刺地称为“析构函数”)来重写 Finalize,以便发现被放弃的对象不会在不被销毁的情况下被销毁。注意,但有机会进行清理。Object.Finalize
的实际操作中有许多棘手的问题,除非绝对必要,否则最好避免依赖它。具有讽刺意义的“析构函数”实际上并不破坏对象,而是延迟它们的破坏。对对象调用 GC.SuppressFinalize() 会将其从放弃时请求通知的对象列表中删除;假设在对象上调用Dispose
,并且依次调用GC.SuppressFinalize()
作为其最后操作,则不存在任何让对象重写 Finalize 或声明“析构函数”尤其有害,但一般来说,最好只在相对简单的类中重写 Finalize(或声明“析构函数”)。Dispose methods, finalizers, and C#'s ironically-named destructors, exist because many objects ask other entities to do things on their behalf until further notice (typically granting exclusive use of something like a file, a region of memory, communications stream, hardware device, GDI handle, etc.); if an object issuing such a request were to disappear without those other entities know their services are no longer required, anything that had been set aside on behalf of the abandoned object would remain uselessly inaccessible.
The
IDisposable.Dispose
method provides a nice consistent way of telling an object that it will no longer be asked to do anything would require the assistance of other entities, and that any other entities which were doing anything on its behalf should be told to stop doing so. IfIDisposable.Dispose
is called properly, objects can minimize the extent to which outside entities have to act on their behalf.Unfortunately, for various reasons (most being easily avoidable; some not), objects are sometimes abandoned without
IDisposable.Dispose
having been called. This will result in outside entities having to uselessly keep acting, at least for awhile, on behalf of the objects which are abandoned. To avoid having outside entities act forever on behalf of outside entities, the system can notify objects that they have been abandoned, thus giving them a chance to inform outside entities of this fact. Whenever an object whose class overridesObject.Finalize
is created, it will be placed in a special list of objects that want to be notified if they are abandoned. Existence on this list, by itself, is not enough to make an object be considered "live", but before the garbage collector removes dead objects from memory it will check whether they are on the notify-before-destruction list. All dead objects which are on the list will be moved from the list of objects requesting notification if/when they are abandoned to a list of objects needing to be notified that they have been abandoned. Placement on this second list will cause the dead objects, and any objects to which they hold references, to be considered "alive" again, at least until the notification has been performed. When they are moved to the second list, however, they are removed from the first list so that if they are later found to be dead again, they will be swept from memory without further notice.After the garbage collection completes, if any objects are in the list of objects needing to be notified of abandonment, the system will call the
Object.Finalize
method of each such object. Typically, onceObject.Finalize
has been called on such an object, there won't be any more rooted references to it and it will vanish on the next garbage collection. It is possible, however, for objects to be resurrected.In vb.net and, so far as I know, most .net languages, one overrides
Object.Finalize
by simply declaring an override in the usual fashion. For whatever reason, the creators of C# decided to forbid that. Instead, in C#, one must use a language structure, ironically called a "Destructor", to overrideFinalize
so that objects which are found to be abandoned will not be destroyed without notice, but instead be given a chance to clean up.There are many tricky wrinkles in the actual operation of
Object.Finalize
, and it is best to avoid relying upon it except when absolutely necessary. The ironically-named "destructors" don't actually destroy objects, but rather delay their destruction. CallingGC.SuppressFinalize()
on an object will remove it from the list of objects requesting notification when they are abandoned; provided thatDispose
is called on an object, and it in turn callsGC.SuppressFinalize()
as its last operation, there isn't any particular harm in having an object overrideFinalize
or declare a "destructor", but in general it's best to only overrideFinalize
(or declare "destructors") in relatively simple classes.