C#一次性问题

时间:2011-04-29 18:42:19

标签: c# garbage-collection idisposable disposable

例如,如果忘记编写IDisposable语句,垃圾收集器是否会自动释放与某些using实例关联的非托管资源(实际上是什么)?< / p>

显然,我不知道何时会发生,但是当我不关心这些资源时,将IDisposable留给GC是没关系的我很好,他们会被处理最终

6 个答案:

答案 0 :(得分:16)

  

例如,如果我忘记使用语句编写,垃圾收集器是否会自动释放与某些IDisposable实例关联的非托管资源(实际上是什么)?

通常,但不一定。一次性资源的作者需要做正确的事。

  

显然,我不知道什么时候会发生这种情况,但是当我不关心那些资源时将IDisposable留给GC是没关系的,我很好,他们最终会被处理掉?

你的问题预示着虚假。 垃圾收集器永远不会调用Dispose。而是垃圾收集器调用析构函数(或者如果你愿意的话,调用“终结器”)。它是析构函数,可能会或可能不会为健忘的用户调用Dispose。

你的问题也表明态度不好。 可能不在乎资源是否被释放,但另一个程序当然可能会关心!如果您的客户正在运行两个程序,这两个程序都试图访问相同的非托管资源,一个由您编写,另一个由其他人编写,该怎么办? 做一个好公民;一旦你完成它们就释放你稀缺的资源,以便其他程序可以使用它们。这就是“使用”的真正目的 - 通过确保快速回收稀缺资源来礼貌。

Dispose模式的正确实现将确保析构函数在用户忘记调用Dispose时清理非托管资源,并确保析构函数在记住时不清理资源。

如果您是为拥有非托管资源的类编写Dispose实现的人,则您有责任在用户未正确调用Dispose的情况下获取析构函数代码。编写此代码的正确方法记录得非常好;根据规律。有关一些有用的链接,请参阅Jon的评论。

另请注意,如果您正在编写此类,那么您还需要使其在不可能的情况下工作,以便用户调用Dispose。例如,假设构造函数分配了两个非托管资源,并且在第一个和第二个资源的分配之间抛出异常并将其捕获到构造函数之外。然后,用户无法调用Dispose,因为在构造函数成功运行后,新对象引用的赋值发生,并且构造函数从未成功运行。如何释放第一个非托管资源?只有析构函数才能释放它。在这种情况下,析构函数必须是健壮的;被破坏的对象可能永远不会被完全构造,所以你不能依赖构造函数的任何不变量。

答案 1 :(得分:3)

是的,他们最终会被清理干净。当您需要确定性资源清理时,您只需要使用using(heh ..),例如GDI,Sql和其他基于系统句柄的代码,或者用于DataTable等可能占用大量内存的对象。 / p>

编辑:这假设您正确实施了Dispose模式并让您的析构函数调用Dispose

答案 2 :(得分:2)

如果一个对象正确地实现了IDisposable接口,通过在终结器中包含对Dispose方法的条件调用,那么GC将在收集期间(通过终结器)触发对象的处理。

有些事情导致这种情况不会发生,但如果遵循标准模式,那么它在大多数情况下都应该有效。

如果对象在终结器中不包含处理代码,则所有投注均已关闭。在太阳超新星之前,未管理的资源可能保持不受限制。

答案 3 :(得分:1)

不,不一定。这取决于你如何获得非托管资源。

如果您对非托管资源(可能是IntPtr)有直接处理,那么您应该拥有终结器或使用SafeHandle ...否则您肯定可以泄漏非托管资源。

如果您的类型只引用FileStream之类的内容,那么您应该期望 类型具有终结器。

现在很少需要从你的类型中直接访问非托管资源,如果你正在使用框架类型,他们真的应该适当地清理(但不确定)。不过,这是可能的。

答案 4 :(得分:0)

系统库中的任何IDisposable在完成时都会自行处理(或者至少会采取与处理相同的大部分步骤)。

无法保证第三方的东西 - 例如,如果您使用某个C库的接口为您提供句柄,那么这些句柄可能无法清理,直到库被卸载(这往往只会发生)在app退出)。但是很常见的是忘记使用Disposeusing(或甚至不知道或不关心某种类型是一次性的),任何没有考虑它的.net库都非常糟糕写的,IMO。

答案 5 :(得分:0)

创建对象时,如果它覆盖Object.Finalize,系统会将其添加到具有已注册终结器的对象列表中。该列表上的对象以及它们直接或间接引用的对象将不符合消除条件,除非或直到GC.SuppressFinalize被调用,否则它们将从列表中删除。如果系统注意到这样的对象有资格消除但是它存在于可终结对象列表上,系统将从具有已注册终结器的对象列表中删除if并将其添加到列表中应该尽快调用Finalize例程的对象。只要后一个列表非空,系统就会执行那里对象的Finalize例程。

请注意,垃圾收集器实际上并不处理任何内容 - 它只是调用已注册完成的对象的Finalize例程。虽然许多类型的对象具有Finalize例程,如果它们被错误地放弃,它们可以确保清理,但最终确定可能充满危险。物体很少在恰当的时间完成;他们通常不会在他们被放弃很久之后才最终确定,并且在某些情况下,他们可能会在他们的资源仍然在使用时最终确定。因此,避免依赖终结者是明智的。在某些情况下,有必要,但他们几乎总是icky。

BTW,请注意,如果某个类的对象直接订阅具有较长使用寿命的对象的事件,则前者对象(事件订阅者)或者他们持有直接或间接引用的任何对象都不符合消除或完成直到后一个对象。虽然有人可能会争辩说,如果被抛弃,所有类都应该自行清理,确保事件订阅者可以发现已经放弃了这些类需要创建和使用包装器对象。在正确使用类的情况下,这将增加相当大的复杂性并损害性能。在不使用包装器对象时尝试使用终结器清理事件将毫无意义;在事件发布者符合条件之前,悬挂事件的类将无法收集资格,一旦发生这种情况,前一类是否取消订阅无关紧要。