给定:
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 行时,有多少对象符合垃圾收集器的条件?
一个。 0 B.1 C.2 D.3 E.4 F.6
答案是 E。为什么?
最佳答案
这个问题,尤其是坚持 4 的那个特定“正确”答案,没有多大意义,因为它得出了几个关于 JVM 如何工作的假设,这些假设在最好的情况下是幼稚的,或者根本是错误的。
它做错的第一件事是假设知道曾经创建了多少对象。
main
方法创建了三个 Icelandic
实例,它们有一个继承字段 weight
,初始化值为 1200L
.值 1200L
使用 Long.valueOf
方法通过自动装箱转换为 Long
实例这是允许的,但不需要缓存经常请求的值。因此,我们无法预测此处创建的 Long
实例的数量,它可能是一个或三个,但如果无论出于何种原因缓存对所有调用都不起作用,甚至两个也是可能的。
然后代码正在处理包含三个实例的局部变量,并且应该在第 14 行“之后”仅包含这些实例中的一个,但是在该行之后没有与此状态可能相关的关联代码。该行之后唯一发生的事情是从 main
方法返回,因此在第 14 行之后所有局部变量都超出范围。
是否有一个执行点可以与“第 14 行之后”相关联但仍在方法 main
中,其中恰好有一个 Icelandic
实例在范围内取决于类文件中是否包含行调试信息以及编译器如何将字节码指令映射到这些行号。
如果编译器插入的 return
字节码指令与第 15 行无关,则在 main
方法中创建的所有实例可能会在第 14 行之后被垃圾回收,但保留请记住,Long
实例可能仍会从全局缓存中引用,因此不符合垃圾回收条件。我们根本无法预测。
JVM 执行还有另一个方面:优化。如果 JVM 对程序语义没有任何影响,则允许 JVM 执行“逃逸分析”并省略对象创建。在您的程序中,从 JVM 的角度来看,所有对象都是未使用的。所以另一个合法的执行场景是这个 main
方法从不创建任何对象,因此根本没有对象符合垃圾收集的条件。
关于java - 垃圾收集的对象数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26263054/