17

在下面的代码中,假设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;是它有资格进行垃圾收集的点。
4

7 回答 7

21

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

   classObject = myObject;

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

   myObject = null;

只去掉一个参考。要成为myObject垃圾收集的候选者,您必须拥有

  classObject = null;
于 2012-10-30T17:56:32.783 回答
6

从书OCA Java SE 7

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

于 2015-08-17T16:31:36.807 回答
4

这实际上由 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>

于 2017-11-15T15:58:36.603 回答
3

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

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

总而言之,您的优化想法在更广泛的情况下不起作用的原因有很多。

于 2012-10-30T18:05:46.437 回答
0

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

于 2012-10-30T17:58:02.847 回答
0

由于您保留myObject引用被维护classObject),它(通过classObject引用的内存中的对象)将不可用于垃圾收集,直到实例被释放/卸载。Test

于 2012-10-30T17:58:02.680 回答
0

在下面的代码中,假设 amethod 已被调用。myObject 最初引用的对象在哪一点/哪一行符合垃圾收集条件?

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

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

于 2013-01-06T11:07:23.290 回答