【问题标题】:number of objects for garbage collection垃圾收集的对象数
【发布时间】:2014-10-08 17:39:23
【问题描述】:

给定:

interface Animal { void makeNoise(); }
class Horse implements Animal {
    Long weight = 1200L;
    public void makeNoise() { System.out.println("whinny"); }
}
public class Icelandic extends Horse {
    public void makeNoise() { System.out.println("vinny"); }
    public static void main(String[] args) {
        Icelandic i1 = new Icelandic();
        Icelandic i2 = new Icelandic();
        Icelandic i3 = new Icelandic();
        i3 = i1; i1 = i2; i2 = null; i3 = i1;  //<-- line 14
     }
 }

当到达第 14 行时,有多少对象符合垃圾收集器的条件?

A. 0 B. 1 C.2 D. 3 E. 4 F.6

答案是 E。为什么?

【问题讨论】:

  • 我们是在谈论 before 还是 after 第 14 行已执行?
  • 第 14 行之后 main 方法关闭

标签: java object inheritance garbage


【解决方案1】:

这个问题,尤其是坚持 4 的特定“正确”答案没有多大意义,因为它对 JVM 的工作方式提出了几个假设,这些假设在最好的情况下是幼稚的,或者完全是错误的。

它做错的第一件事是假设知道曾经创建了多少对象。

main 方法创建了三个 Icelandic 实例,其继承字段 weight 初始化为 1200L。值1200L 使用Long.valueOf 方法通过自动装箱转换为Long 实例,这是允许的,但不需要缓存频繁请求的值。所以我们无法预测这里创建的Long 实例的数量,可能是一到三个,但如果缓存不适用于所有调用,无论出于何种原因,甚至两个也可能。

然后代码正在使用保存三个实例的局部变量,并且应该在第 14 行“之后”只保存这些实例中的一个,但在该行之后没有与此状态相关的相关代码。该行之后发生的唯一事情是从 main 方法返回,因此在第 14 行之后,所有局部变量都超出了范围。

是否存在可以与“第 14 行之后”相关联但仍在方法 main 中的执行点,其中 Icelandic 的一个实例在范围内,这取决于行调试信息是否已包含在class 文件以及编译器如何将字节码指令映射到这些行号。

如果编译器插入的 return 字节码指令与第 15 行无关,则在 main 方法中创建的所有实例可能会在第 14 行之后被垃圾回收,但请记住 Long 实例可能仍然从全局缓存中引用,因此没有资格进行垃圾收集。我们根本无法预测。

JVM 执行还有另一个方面:优化。如果 JVM 对程序语义没有任何影响,则允许 JVM 执行“转义分析”并省略对象创建。在您的程序中,从 JVM 的角度来看,所有对象都是未使用的。所以另一个合法的执行场景是这个main方法永远不会创建任何对象,然后根本没有对象可以进行垃圾回收。

【讨论】:

    【解决方案2】:

    所以每个对象都有一个 Long 对象,所以当每个冰岛对象都被标记为垃圾回收时,它指向的 Long 值也是如此。因此之后:

    Icelandic i1 = new Icelandic();
    Icelandic i2 = new Icelandic();
    Icelandic i3 = new Icelandic();
    i3 = i1; i1 = i2; i2 = null; i3 = i1;
    

    原来的i3对象丢失了,原来的i1值也丢失了。 i1 和 i3 都指向原始的 i2 使其保持活动状态(并且 i2 指向 null,不保持活动状态)。这会留下 2 个对象被标记为垃圾回收(原始 i1 和原始 i3)以及它们指向的 Long 值,从而为您提供 4 个标记为垃圾回收的项目。

    【讨论】:

    • +1。但是,扮演魔鬼的拥护者,我认为正确的答案实际上是“3 或 4,取决于实施”。尽管 JLS 声明 -128 和 127 之间的原始值必须始终被保留,因此是同一个对象,但它也指出“理想情况下,装箱给定原始值 p,将始终产生相同的引用。”因此,一个给定的兼容 JVM 实现很容易导致这些 Long 值是同一个对象,在这种情况下,只有三个实际对象有资格被收集!
    • @berry120 不会是 2 或 4 吗?如果所有三个 Long 对象都引用同一个对象,则只有两个 Icelandic 对象会获得 GCd,因为它们的两个 weights 都指向同一个 Long 引用,而活着的 Icelandic 仍在使用。跨度>
    • 我同意这不是一个好问题,但鉴于 OP 给出了正确的答案,我相信这是支持它的理由。
    • @KevinDiTraglia 哦,当然,我只是把它作为附录指出来!
    【解决方案3】:

    我会选择符合条件的冰岛语 2 对象(对象 1 和 3)。 由于每个冰岛人都持有一个 Long 对象,因此它给了我们 4 个。

    【讨论】:

      【解决方案4】:

      Line14之后,只有原来的i2对象是存活的,没有referenceemce指向原来的i1和i3,所以它们有资格被垃圾收集器,而且这个对象有一个基类型,一个长字段,丢给它,所以2 *2 = 4;

      【讨论】:

        【解决方案5】:

        重要的东西在这里

        Icelandic i1 = new Icelandic();
        Icelandic i2 = new Icelandic();
        Icelandic i3 = new Icelandic();
        i3 = i1; i1 = i2; i2 = null; i3 = i1;
        

        所以i3 立即失去其引用,因此原始对象已准备好进行收集。

        i1i3 引用 i2 所以 i1 的原始对象已准备好收集。

        然后i2 失去它的引用,所以所有 3 都是 null 并被垃圾回收。

        我个人会选择 3。这是一个非常发人深省的问题。

        【讨论】:

          猜你喜欢
          • 2010-11-08
          • 1970-01-01
          • 2012-10-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多