出于好奇,我想知道是否存在垃圾收集器无法运行或根本不运行(可能是由于异常)的情况?
如果是,很可能会有 OutOfMemory/ Stackoverflow 异常。那么在这种情况下,只需查看异常消息、堆栈跟踪等,我们就可以确定 gc 无法运行的核心问题。
出于好奇,我想知道是否存在垃圾收集器无法运行或根本不运行(可能是由于异常)的情况?
如果是,很可能会有 OutOfMemory/ Stackoverflow 异常。那么在这种情况下,只需查看异常消息、堆栈跟踪等,我们就可以确定 gc 无法运行的核心问题。
正如其他人所提到的,有很多事情可以阻止 GC 运行。FailFast 快速失败;在建筑物被拆除之前,它不会停止倒垃圾。但是您专门询问了例外情况。
未捕获的异常会产生实现定义的行为,因此最终块是否运行、垃圾收集是否运行以及终结器队列对象是否在存在未捕获的异常时完成是实现定义的。发生这种情况时,允许 CLR 的实现做任何事情,“任何事情”包括“运行 GC”和“不运行 GC”。事实上,CLR 的实现已经随着时间的推移改变了它们的行为。在 CLR 的 v1.0 中,终结器线程上的未捕获异常取出了进程,在 v2.0 中,终结器线程上的未捕获异常被捕获,错误被记录,并且终结器继续运行。
有四个感兴趣的问题:
有什么东西会导致程序完全死掉,而垃圾收集器没有机会运行吗
有什么东西可以阻止垃圾收集器运行而不导致系统完全死机
可以阻止对象的终结器运行而不导致系统完全死机吗
异常可以使对象在任意时间段内无法收集吗
关于第一个,答案是“肯定”。可能发生的方式有很多,这里没有必要一一列举。
关于第二个问题,答案是“一般不会”,因为垃圾收集器的故障会导致程序瘫痪;然而,在某些情况下,程序中不使用 GC 管理内存的部分可能能够继续运行,即使使用托管对象的部分可能会被无限期地阻塞。
关于第三个问题,以前在.net 中,finalizer 中的异常可能会干扰其他 finalizer 的操作,而不会杀死整个应用程序;自 .net 2.0 以来,此类行为已更改,因此终结器抛出的未捕获异常通常会杀死整个程序。然而,在编写不佳的终结器中抛出并捕获的异常可能会导致它无法清理它应该清理的所有内容,从而导致问题 #4。
关于第四个问题,对象在创建时建立对自己的长期(可能是静态的)引用,并在清理代码中销毁这些引用是很常见的。如果异常阻止清理代码按预期运行,则可能会导致对象变得无法收集,即使它们不再有用。
是的,在 Java 中曾经有这样一种情况,即程序可能会在没有最后一次运行 GC 的情况下停止 - 在大多数情况下,这是可以的,因为当程序的堆被销毁时,所有内存都被清除了,但是你可以拥有对象没有运行它们的终结器的问题,这对您来说可能是也可能不是问题,这取决于这些终结器会做什么。
我怀疑您是否能够确定 GC 失败,因为该程序将以不干净的方式像鹦鹉一样死,因此您甚至可能不会获得堆栈跟踪。您也许可以对其进行事后调试(如果您打开了正确的 dbg 设置,那么 .NET 在与出色的 Windows 调试工具很好地配合使用时是非常糟糕的)。
在某些边缘情况下finally
块不会执行 - 调用FailFast是一种情况,其他情况请参见此处的问题。
鉴于此,我想在某些情况下(尤其是在using
语句/IDisposable
对象中),块中发生的资源清理/垃圾收集finally
不会被执行。
更明确地说,是这样的:
try
{
//new up an expensive object, maybe one that uses native resources
Environment.FailFast(string.Empty);
}
finally
{
Console.WriteLine("never executed");
}