垃圾收集保证

时间:2010-01-07 01:49:19

标签: c# garbage-collection

对垃圾收集器有什么保证?

根据我的研究,我找到了:

  • 如果仍有对内存的引用,则不会进行垃圾回收
  • 如果没有参考:
    • 当GC是非确定性的时候
    • 当GC释放到终结器中时,将在释放内存之前运行。
    • 无法保证终结者的顺序(所以不要假设父母会在孩子面前运行。)

但我真正想知道的是:

是否保证所有内存最终都将被垃圾收集并且终结器(析构函数)在对象上运行(假设程序退出很好)。例如,最终退出时没有内存压力的应用程序会强制GC查找所有对象并确保调用终结器(析构函数)(包括静态成员变量)?

我确实在此页面上找到了引用:     http://www.c-sharpcorner.com/UploadFile/tkagarwal/MemoryManagementInNet11232005064832AM/MemoryManagementInNet.aspx

  

此外,默认情况下,当应用程序退出时,不会为无法访问的对象调用Finalize方法,以便应用程序可以快速终止。

但我不确定这句话有多权威。

我还发现了以下文档:    CriticalFinalizerObject

5 个答案:

答案 0 :(得分:3)

您应该编写终结器的唯一时间是构建一个类型来处理类型的非托管资源。例如,在业务应用程序中使用Sql Server的数据访问层在任何地方都不需要终结器,即使涉及非托管数据库连接,因为基本的SqlConnection类已经根据需要完成了这些连接。但是如果你从头开始构建一个全新的数据库引擎,其连接限制与sql server类似,并且正在为它实现ado.net提供程序,那么连接类型应实现终结器以确保尽可能释放您的连接。

但是,除了流程结束时发生的事情,你没有得到任何保证。

更新:

鉴于此背景:

  

我正在和一位同事讨论我对他的代码进行的代码审查。他坚持认为析构函数可以被称为对象。我不同意(但不确定)并且更喜欢使用IDisposable。

批评使用析构函数/终结器是正确的。正如我上面所说,你应该只在使用真正新的非托管资源时使用它们。不仅仅是资源的实例,而是您正在使用的资源类型。

对于包装“普通”非托管资源(如SqlConnection,TcpClient等)的代码,IDisposable是更好的选择。然后,您知道一旦调用Dispose()而不需要等待收集类型,就会清除资源。如果没有人调用Dispose()(这可能是你同事的关注),那么当你的新类型被收集时,你正在收集的非托管资源的原始类型的实例也应该能够被收集,并且它是终结者将释放资源。

您需要带来的主要事情是,在收集对象之前,无法调用终结器。你必须等待垃圾收集器,这意味着你可能会更长时间地打开资源。 IDisposable允许您立即释放它。当然你可以做到这两点,但这听起来不像这里发生的事情,如果你确实有两者,你必须小心不要与原始类型的终结器冲突,否则你可能会导致不必要的有害异常。实际上,你自己的终结器实现在这里是多余的,并增加了不必要的复杂性。

最后,我不得不对此声明提出异议:

  

如果仍有对内存的引用,则不会进行垃圾回收

可以引用一个对象,它仍然会被收集。重要的是,如果对象可达:是对象 rooted 的任何引用。例如,您可能有一个包含多个对象的列表。该列表超出了范围。显然,仍然存在对列表中所有对象的引用,但它们仍然可以在GC的第一次传递中收集,因为引用不再是有根的。

答案 1 :(得分:3)

  

是否保证所有内存   最终将被垃圾收集   并且终结器(析构函数)继续运行   对象(假设程序   很好地退出了。)

没有。从Object.Finalize文档中可以清楚地看出,如果

,则不能调用终结器
  • 其他一些终结器无法正常完成:

      

    另一个终结器无限期阻止   (进入一个无限循环,试图   获得一个永远无法获得的锁   等等)。因为运行时尝试   运行终结器完成,其他   如果a,可能不会调用终结器   终结者无限期地阻止。

  • 其他一些终结者创造了更多 制作它的最终目标 不可能完成所有 可终结的对象:

      

    运行时继续Finalize   关闭期间的对象只有在   可终结对象的数量   继续减少。

  • 其他一些终结者会抛出异常:

      

    如果Finalize或覆盖Finalize   抛出异常和运行时   不是由应用程序托管的   覆盖默认策略   运行时终止进程,没有   积极的尝试 - 终结块或   终结者被执行。这种行为   确保过程完整性   终结者无法释放或毁灭   资源。

话虽如此,除非有必要,否则你有更多理由不想使用终结器。

  • 他们放慢了垃圾收集器的速度 (甚至可以减慢它 记忆不是那么多 回收速度和用完一样快。)
  • 他们在另一个线程上运行,带来 多线程问题发挥作用。
  • 他们没有被执行 确定性秩序。
  • 他们可以复活那些对象 已经敲定(而且不会 除非明确地再次确定 重新注册完成)。

答案 2 :(得分:1)

Spec中的1.6.7.6说:

  

1.6.7.6析构函数

     

析构函数是实现操作的成员   需要破坏一个实例   类。破坏者不能拥有   参数,他们不能拥有   辅助功能修饰符,以及它们   无法显式调用。该   调用实例的析构函数   在垃圾过程中自动   集合。

     

垃圾收集器是   在决定何时允许宽阔的自由度   收集对象并运行   析构函数。具体来说,时机   析构函数调用不是   确定性和析构可能是   在任何线程上执行。对于这些和   其他原因,课程应该   只有在没有时才实现析构函数   其他解决方案也是可行的。

     

在   using语句提供了更好的   对象破坏的方法。

所以不,不能保证他们被召唤。

答案 3 :(得分:0)

根本不会调用终结器的唯一时间是强行卸载AppDomain。
一般来说,您不必担心它。

答案 4 :(得分:0)

无法保证。

如果您的流程能够很好地终止某些定义,那么可能会有一个保证。但是有很多事情可以发生:

  • 电源故障
  • 流程以“强硬”或“强制”方式终止
  • 非托管线程抛出调用OS exit()函数或抛出异常
  • 调用System.Environment.FailFast,执行:

MSDN:“终止进程但不执行任何活动的try-finally块或终结器。”