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

考虑下面的 C # 程序,我将它作为不循环创建循环的答案提交给 codegolf:

class P{
static int x=0;
~P(){
System.Console.WriteLine(++x);
new P();
}
static void Main(){
new P();
}
}

在我的检查中,这个程序看起来像一个无限循环,但它似乎运行了几千次,然后程序成功终止,没有错误(没有抛出错误)。最终没有调用 P的终结器是否违反了规范?

显然,这是愚蠢的代码,永远不应该出现,但我很好奇,该程序如何能够完成。

原代码高尔夫职位: Https://codegolf.stackexchange.com/questions/33196/loop-without-looping/33218#33218

4947 次浏览

The finalizer doesn't run in the main thread. The finalizer has its own thread that runs code, and it's not a foreground thread that would keep the application running. The main thread completes effectively right away, at which point the finalizer thread simply runs as many times as it gets a chance to before the process gets torn down. Nothing is keeping the program alive.

A garbage collector is not an active system. It runs "sometimes" and mostly on demand (for instance when all pages offered by the OS are full).

Most garbage collectors run in a breadth-first generation-like manner in a subthread. In most cases it can take hours before the object is recycled.

The only problem occurs when you want to terminate the program. However that's not really a problem. When you use kill an OS will ask politely to terminate processes. When the process however remains active, one can use kill -9 where the Operating System removes all control.

When I ran your code in the interactive csharp environment, I've got:

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

Thus your program crashes because stdout is blocked by the termintation of the environment.

When removing the Console.WriteLine and killing the program. It after five second the program terminates (in other words, the garbage collector gives up and simply will free all memory without taking finalizers into account).

As per Richter in the second edition of CLR via C# (yes I need to update):

Page 478

For (The CLR is shutting down) each Finalize method is given approximately two seconds to return. If a Finalize method doesn't return within two seconds, the CLR just kills the process - no more Finalize methods are called. Also, if it takes more then 40 seconds to call all objects' Finalize methods, again, the CLR just kills the process.

Also, as Servy mentions, it has its own thread.