C# 垃圾收集器如何在这里避免无限循环?

C# 垃圾收集器如何在这里避免无限循环?,c#,garbage-collection,C#,Garbage Collection,考虑一下下面的C#程序,我在codegolf上提交了它,作为在不循环的情况下创建循环的答案: class P{ static int x=0; ~P(){ System.Console.WriteLine(++x); new P(); } static void Main(){ new P(); } } 在我的检查中,这个程序看起来像一个无限循环,但它似乎运行了数千次迭代,然后程序成功地终止,没有错误(没有

考虑一下下面的C#程序,我在codegolf上提交了它,作为在不循环的情况下创建循环的答案:

class P{
    static int x=0;
    ~P(){
        System.Console.WriteLine(++x);
        new P();
    }
    static void Main(){
        new P();
    }
}
在我的检查中,这个程序看起来像一个无限循环,但它似乎运行了数千次迭代,然后程序成功地终止,没有错误(没有抛出错误)。最终没有调用
P
的终结器是否违反了规范

显然,这是愚蠢的代码,永远不会出现,但我很好奇这个程序是如何完成的

原邮政编码::

终结器不在主线程中运行。终结器有自己的线程来运行代码,而不是前台线程来保持应用程序运行。主线程立即有效地完成,在这一点上,终结器线程只需在进程被破坏之前尽可能多地运行。没有任何东西使程序保持活动状态。

垃圾收集器不是活动系统。它“有时”运行,大部分按需运行(例如,当操作系统提供的所有页面都已满时)

大多数垃圾收集器在子线程中以广度第一代的方式运行。在大多数情况下,回收物品可能需要数小时

唯一的问题是当您想终止程序时。然而,这并不是一个真正的问题。当您使用
kill
时,操作系统会礼貌地要求终止进程。但是,当进程保持活动状态时,可以使用
kill-9
,操作系统将删除所有控制

当我在交互式
csharp
环境中运行您的代码时,我得到:

csharp>  

1
2

Unhandled Exception:
System.NotSupportedException: Stream does not support writing
  at System.IO.FileStream.Write (System.Byte[] array, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.StreamWriter.FlushBytes () [0x00000] in <filename unknown>:0 
  at System.IO.StreamWriter.FlushCore () [0x00000] in <filename unknown>:0 
  at System.IO.StreamWriter.Write (System.Char[] buffer, Int32 index, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.CStreamWriter.Write (System.Char[] buffer, Int32 index, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.CStreamWriter.Write (System.Char[] val) [0x00000] in <filename unknown>:0 
  at System.IO.CStreamWriter.Write (System.String val) [0x00000] in <filename unknown>:0 
  at System.IO.TextWriter.Write (Int32 value) [0x00000] in <filename unknown>:0 
  at System.IO.TextWriter.WriteLine (Int32 value) [0x00000] in <filename unknown>:0 
  at System.IO.SynchronizedWriter.WriteLine (Int32 value) [0x00000] in <filename unknown>:0 
  at System.Console.WriteLine (Int32 value) [0x00000] in <filename unknown>:0 
  at P.Finalize () [0x00000] in <filename unknown>:0
csharp>
1.
2.
未处理的异常:
System.NotSupportedException:流不支持写入
在System.IO.FileStream.Write(System.Byte[]数组,Int32偏移量,Int32计数)[0x00000]中:0
位于:0中的System.IO.StreamWriter.FlushBytes()[0x00000]处
位于:0中的System.IO.StreamWriter.FlushCore()[0x00000]处
在System.IO.StreamWriter.Write(System.Char[]缓冲区,Int32索引,Int32计数)[0x00000]中:0
在System.IO.CStreamWriter.Write(System.Char[]缓冲区,Int32索引,Int32计数)[0x00000]中:0
在System.IO.CStreamWriter.Write(System.Char[]val)[0x00000]处写入:0
在System.IO.CStreamWriter.Write(System.String val)[0x00000]处写入:0
在System.IO.TextWriter.Write(Int32值)[0x00000]中写入:0
在System.IO.TextWriter.WriteLine(Int32值)[0x00000]处输入:0
在0中的System.IO.SynchronizedWriter.WriteLine(Int32值)[0x00000]处
在System.Console.WriteLine(Int32值)[0x00000]中:0
在第页完成()[0x00000]中:0
因此,您的程序会崩溃,因为
stdout
被环境的终止阻止


删除
控制台时。WriteLine
并终止程序。五秒钟后程序终止(换言之,垃圾收集器放弃并将释放所有内存,而不考虑终结器)。

根据第二版CLR中通过C#的里希特(是的,我需要更新):

第478页

对于(CLR正在关闭),每个Finalize方法都有大约两秒钟的时间返回。如果Finalize方法没有在两秒钟内返回,CLR只会终止进程-不再调用Finalize方法。此外,如果调用所有对象的Finalize方法需要40秒以上,CLR也会终止该进程


另外,正如Servy提到的,它有自己的线程。

我害怕运行它。不调用终结器是错误的。我不知道为什么要运行几千次迭代,我希望零调用。CLR有保护,防止终结器线程永远无法完成它的工作。它会在2秒后强制终止。因此,你在标题中的问题的真正答案是,它通过让无限循环运行40秒来避免它,然后它被终止。通过尝试,程序似乎在2秒后不管发生什么都会杀死所有东西。实际上,如果继续生成线程,它将持续更长的时间:)如果终结器在程序应该退出后40秒没有完成,因为没有主线程处于活动状态,它将被终止,进程将终止。这些都是旧值,所以微软可能已经调整了实际数字,甚至是整个算法。Se@LasseV.Karlsen是语言的记录行为,还是MS选择如何实现其终结器作为实现细节?我期待后者,我也期待后者。我所见过的关于这种行为的最正式的参考是Eric在他的回答中发布的内容,摘自Jeffrey Richter的《CLR via C#》一书。有趣的是,互动式csharp因为完全不同的原因爆炸了。原始程序段没有控制台写线,我很好奇它是否也会终止。@MichaelB:我也测试过这个(见下面的注释)。它等待五秒钟,然后终止。我猜第一个
P
实例的终结器只是超时了。在这段代码中,每个finalize方法对每个对象花费的时间明显少于40秒。创建一个新对象并符合终结条件与当前终结器无关。这实际上不是完成作业的原因。在关闭时,freachable队列也会超时清空。这就是这段代码失败的地方,它不断向该队列添加新对象。想想看,清空freachable队列不是和“另外,如果调用所有对象的Finalize方法需要40秒以上,CLR也会终止进程”一样吗