何时该对象符合垃圾收集条件?

在下面的代码中,假设已经调用了amethod 。 在什么点/行是myObject最初引用的对象,有资格获得垃圾收集?

 class Test { private Object classObject; public void amethod() { Object myObject = new Object(); classObject = myObject; myObject = null; } } 

如果classObjectamethod具有public,protected,default或static的访问修饰符,它会影响Object符合垃圾收集的条件吗? 如果是这样,它将如何受到影响?

  • 我首先想到的是,当Test对象符合垃圾收集条件时,Object符合垃圾收集条件。
  • 但话又说回来。 优化器可能知道从不读取classObject,在这种情况下classObject = myObject; 将被优化出来并且myObject = null; 是它有资格获得垃圾收集的点。

在丢弃对它的所有引用之前,该对象不会成为垃圾收集的候选对象。 Java对象是通过引用分配的,所以当你有

  classObject = myObject; 

您为堆上的同一对象分配了另一个引用。 所以这一行

  myObject = null; 

只删除一个引用。 要使myObject成为垃圾收集的候选者,您必须拥有

  classObject = null; 

您的想法是私有对象可能立即被GC,因为没有其他代码能够访问它确实有一些牵引力,但这会混淆Java内存管理的一般语义。 例如,如果该对象实现了finalize ,并且Java语义清楚地规定了对象何时有资格进行垃圾收集,那么必须根据规范调用终结器方法。

另请注意,对象反过来可能会引用其他对象,甚至可能会产生更复杂的结果。 更不用说随时可以通过Reflection访问该对象,即使没有代码可以进行该分配,观察到的字段突然变为null也没有任何意义。

总而言之,有很多原因可以解释为什么你的优化思想不能在更广泛的范围内发挥作用。

这实际上是通过Java语言规范§12.6.1,实现最终化来实现的 :

可以设计优化程序的转换,以减少可达到的对象的数量,使其少于可以被认为可达的对象的数量。 例如,Java编译器或代码生成器可以选择设置将不再用于null的变量或参数,以使得此类对象的存储可能更快地被回收。

如果对象字段中的值存储在寄存器中,则会出现另一个示例。 然后程序可以访问寄存器而不是对象,并且永远不会再次访问对象。 这意味着该对象是垃圾。 …

…请注意,仅当引用位于堆栈上且未存储在堆中时,才允许此类优化。

例如,考虑Finalizer Guardian模式:

  class Foo { private final Object finalizerGuardian = new Object() { protected void finalize() throws Throwable { /* finalize outer Foo object */ } } } 

如果子类重写finalize并且没有显式调用super.finalize ,则终结器监护人会强制调用super.finalize

如果允许对存储在堆上的引用进行这些优化,那么Java编译器可以检测到finalizerGuardian字段永远不会被读取,将其清空,立即收集对象,并尽早调用终结器。 这与意图背道而驰:程序员可能想在Foo实例无法访问时调用Foo终结器。 因此,这种转换不合法:只要外部类对象可以访问,内部类对象就应该可以访问。

此示例可以1:1应用于您的示例,只要该对象由实例字段classObject ,它就不能比包含该引用的Test实例更早地收集垃圾。

但请注意,当使用Test实例应用于代码时,仍然允许规范中提到的积极优化。 只要两者一起收集Test实例和引用的对象,就可能发生早于预期的收集。 在这种情况下, §12.6中规定的以下方面适用:

Java编程语言对finalize方法调用没有任何排序。 终结器可以按任何顺序调用,甚至可以同时调用。

因此,完全有可能早于classObject引用的对象收集Test实例,而之前调用“inner”对象的终结器。 唯一可以保证的是,当内部对象的终结器运行时,外部对象无法访问(或者具有挂起或并发终结)。 因为在你的例子中,既没有非平凡的终结器,也无论如何……

在下面的代码中,假设已经调用了amethod。 在什么点/行是myObject最初引用的对象,有资格获得垃圾收集?

您的问题是荒谬的,因为您的高级源代码与垃圾收集器看到的低级表示(寄存器,堆栈和全局变量中的全局根)之间存在脱节。

你的短语“有资格进行垃圾收集”可能意味着堆分配的内存块在什么时候无法访问。 因此,只能通过对堆分配的内容以及生成的代码保留引用的时间进行大量(可疑的)假设来回答您的问题。

来自Book OCA Java SE 7

当对象无法再访问时,该对象被标记为有资格被垃圾收集,这可能在对象超出范围时发生。 当对象的引用变量被赋值为显式空值或被重新初始化时,也会发生这种情况。

这里没有对象符合垃圾收集的条件,因为您正在为同一个对象创建两个引用,并且您只为一个引用提供null,但是其他引用仍然指向您的对象

由于您在classObject中保持myObject引用被维护 ),因此在释放/卸载Test实例之前,它(通过classObject引用的内存中的对象)将无法用于垃圾收集。