什么时候对象符合垃圾收集条件?

时间:2012-10-30 17:54:15

标签: java garbage-collection

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

class Test {
  private Object classObject;

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

如果classObjectamethod具有公共,受保护,默认或静态的访问修饰符,是否会影响对象符合垃圾收集的条件?如果是这样,它将如何受到影响?

  • 我的第一个想法是,当Test对象符合垃圾收集条件时,该对象符合垃圾收集的条件。
  • 但是又一次。优化器可能知道从不读取classObject,在这种情况下classObject = myObject;将被优化,myObject = null;是符合垃圾收集条件的点。

7 个答案:

答案 0 :(得分:18)

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

   classObject = myObject;

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

   myObject = null;

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

  classObject = null;

答案 1 :(得分:3)

这实际上是由Java语言规范§12.6.1, Implementing Finalization 精确解决的:

  

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

     

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

<强>但是

  

...请注意,只有当引用存在于堆栈中而不是存储在堆中时,才允许这种优化。

     
    

例如,考虑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实例应用于代码时,仍然允许规范中提到的积极优化。只要两者Test实例和引用的对象被收集在一起,就可能发生早于预期的集合。在这种情况下,§12.6中指定的以下方面适用:

  

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

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

答案 2 :(得分:2)

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

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

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

答案 3 :(得分:1)

来自Book OCA Java SE 7

  

对象被标记为符合条件以进行垃圾回收   无法再访问,这可能在对象熄灭时发生   范围。当对象的引用变量是时,它也可能发生   分配了显式空值或重新初始化。

答案 4 :(得分:0)

此处没有对象符合垃圾回收的条件,因为您正在为同一个对象创建两个引用,并且您只向一个引用赋予null,但其他引用仍然指向您的对象

答案 5 :(得分:0)

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

答案 6 :(得分:0)

  

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

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

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