【问题标题】:Objects eligible for garbage collection符合垃圾回收条件的对象
【发布时间】:2012-07-19 15:27:18
【问题描述】:

这个问题来自Kathy SierraSCJP 1.6。有多少对象符合垃圾回收条件?

根据 Kathy Sierra 的回答,它是 C。这意味着两个对象有资格进行垃圾回收。我已经给出了答案的解释。 但是为什么c3 不符合garbage collection (GC) 的条件?

class CardBoard {
    Short story = 200;
    CardBoard go(CardBoard cb) {
    cb = null;
    return cb;
}

public static void main(String[] args) {
    CardBoard c1 = new CardBoard();
    CardBoard c2 = new CardBoard();
    CardBoard c3 = c1.go(c2);
    c1 = null;
    // Do stuff
} }

当达到// Do stuff 时,有多少对象符合GC 条件?

  • 答:0
  • 乙:1
  • C: 2
  • D:编译失败
  • E:不可能知道
  • F: 运行时抛出异常

答案:

  • C 是正确的。只有一个 CardBoard 对象 (c1) 符合条件,但它有一个关联的 Short 包装对象也符合条件。
  • A、B、D、E 和 F 根据上述不正确。 (目标 7.4)

【问题讨论】:

  • 严格来说c3 不能满足GC,因为它不是一个对象。它是一个变量,可以指向一个对象。
  • 正确答案是all of them...

标签: java scjp


【解决方案1】:

让我们逐行分解:

CardBoard c1 = new CardBoard();

我们现在有两个对象,CardBoard c1 指向和 Short c1.story。两者都不适用于 GC,因为c1 指向CardBoardCardBoardstory 变量指向Short...

CardBoard c2 = new CardBoard();

与上面类似,我们现在有四个对象,没有一个可用于 GC。

CardBoard c3 = c1.go(c2);

我们调用 c1 指向的 CardBoard 上的 go 方法,传递 c2 的值,这是对 CardBoard 对象的引用。我们将参数设为空,但 Java 是按值传递的,这意味着 c2 变量本身不受影响。然后我们返回空参数。 c3nullc1c2 不受影响。我们还有 4 个对象,没有一个可以被 GC。

c1 = null;

我们将 c1 设为空。 c1 之前指向的 CardBoard 对象现在没有任何指向它,它可以被 GC 处理。因为CardBoard 对象内的story 变量是唯一指向Short 的东西,并且因为CardBoard 对象符合GC 条件,所以Short 也符合GC 条件。这给了我们 4 个对象,其中 2 个可以被 GC 处理。符合 GC 条件的对象是之前被 c1c1.story 引用的对象。

【讨论】:

    【解决方案2】:

    c3 指向的对象从未存在过。构造函数只被调用了两次,两个对象,c1c2 分别指向一个对象。 c3 只是一个引用,除了空指针之外从未分配过任何东西。

    当前指向 null 的引用 c3 不会超出范围并从堆栈中删除,直到跨过 main 方法末尾的右大括号。

    原来分配给c1的对象是不可访问的,因为c1引用被设置为null,但是c2引用并没有改变,所以分配给它的对象仍然可以通过@在这个范围内访问987654330@参考。

    【讨论】:

    • 有趣的是,有这么多教程和练习讨论这些事情,但所有这些都是错误的。事实上,JVM 根本不知道局部变量的范围。过去,这通常意味着尽管超出范围,但某些引用并未被清除。今天,您可能会惊讶于对象在“范围内”被收集的事实,请参阅“finalize() called on strongly reachable object in Java 8”...
    • 1) 问题是关于 java 6 2) 如果不知道“//do stuff”可能包含什么,就不可能推测编译器可能对 c2 的未来可达性做出什么优化结论。
    • Java 6 和 Java 8 之间的规则没有改变。你甚至不能排除在实际的 Java 6 实现中会发生这样的事情,只是到目前为止还没有在 SO 上讨论过。如果您假设// do stuff 可能包含与可达性相关的操作,那么正确的答案是问题不完整。它只是证明了更多,这样的问题是多么毫无意义......
    【解决方案3】:

    c3null,因此显然没有符合垃圾回收条件的对象。

    请注意,只创建了两个CardBoard 对象,这两个在这些行中:

    CardBoard c1 = new CardBoard();
    CardBoard c2 = new CardBoard();
    

    在引用杂耍之后,只有一个是没有引用的。

    【讨论】:

      【解决方案4】:

      正式的正确答案是我们不知道。而我们不知道的原因是这一行:

      Short story = 200;
      

      这将编译为以下字节码:

      CardBoard();
      Code:
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: sipush        200
         8: invokestatic  #2                  // Method java/lang/Short.valueOf:(S)Ljava/lang/Short;
        11: putfield      #3                  // Field story:Ljava/lang/Short;
        14: return
      

      第 8 行是这里的关键,Short.valueOf(),它返回原始 200 的盒装等效项。再来看看Short.valueOf()的Javadoc:

      此方法将始终缓存 -128 到 127 范围内的值, 包括在内,并且可能会缓存此范围之外的其他值。

      200 超出了“必须缓存”范围,因此属于“可能缓存”。如果它被缓存,当它包含CardBoard 实例时,story 的值将不符合 GC 条件。如果没有缓存,story 将无法访问,因此被 GC。

      为了使问题明确(并且建议的答案正确),代码应该修改如下:

      Short story = new Short(200);
      

      更新:Short.valueOf()1.6 Javadoc 比我引用的 1.8 版本更神秘,但同样的逻辑适用:无法仅通过查看代码来判断是否将返回一个新的或缓存的 Short 实例。

      【讨论】:

        【解决方案5】:

        如果您注意到代码中只创建了两个对象。 c3 从未初始化为对象,它是一个空引用。因此,只有一个“对象”符合垃圾回收条件。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-01-20
          相关资源
          最近更新 更多