这实际上由 Java 语言规范 第 12.6.1 节,实现终结精确解决:
可以设计优化程序的转换,将可到达的对象的数量减少到比那些天真地认为是可到达的要少。例如,Java 编译器或代码生成器可能会选择设置一个不再使用的变量或参数,null
以使此类对象的存储可能更快地被回收。
如果对象字段中的值存储在寄存器中,则会出现另一个示例。然后程序可能会访问寄存器而不是对象,并且永远不会再次访问对象。这意味着该对象是垃圾。…</p>
但
…请注意,只有当引用在堆栈中而不是存储在堆中时,才允许进行这种优化。
例如,考虑 Finalizer Guardian 模式:
class Foo {
private final Object finalizerGuardian = new Object() {
protected void finalize() throws Throwable {
/* finalize outer Foo object */
}
}
}
super.finalize
如果子类覆盖并且finalize
未显式调用super.finalize
.
如果允许对存储在堆上的引用进行这些优化,那么 Java 编译器可以检测到该finalizerGuardian
字段从未被读取,将其清空,立即收集对象,并尽早调用终结器。这与意图背道而驰:程序员可能想在实例无法访问Foo
时调用终结器。Foo
因此,这种转换是不合法的:只要外部类对象可访问,内部类对象就应该是可访问的。
此示例可以 1:1 应用于您的示例,只要该对象被实例字段引用classObject
,它就不会比Test
包含该引用的实例更早地进行垃圾收集。
但是,请注意,在将规范中提到的激进优化应用于使用Test
实例的代码时,仍然是允许的。只要将实例和引用的对象都收集在一起,就可能发生早于预期的收集。在这种情况下,第12.6 节Test
中规定的以下方面适用:
Java 编程语言没有对 finalize 方法调用进行排序。终结器可以按任何顺序调用,甚至可以同时调用。
So it’s perfectly possible that the Test
instance is collected earlier than the object referenced by classObject
whereas the “inner” object’s finalizer is invoked earlier. The only thing that is guaranteed, is, that when the inner object’s finalizer runs, the outer object is unreachable (or has a pending or concurrent finalization). Since in your example, neither has a non-trivial finalizer, that doesn’t matter anyway…</p>