3. class Beta{ }
4. class Alpha {
5. static Beta b1;
6. Beta b2;
7. }
8. public class Tester {
9. public static void main(String[] args) {
10. Beta b1 = new Beta(); Beta b2 = new Beta();
11. Alpha a1 = new Alpha(); Alpha a2 = new Alpha();
12. a1.b1 = b1;
13. a1.b2 = b1;
14. a2.b2 = b2;
15. a1 = null; b1 = null; b2 = null;
16. // do stuff
17. }
18. }
我认为有2个对象可用于垃圾回收
4条答案
按热度按时间8zzbczxx1#
答案取决于Java实现...以及你选择如何解释这个问题。特别是第16行到底是什么意思?
a2
中的值,并且可能使用也可能不使用(静态)变量Alpha.b1
中的值。根据Java 6的JLS(12.6.1):
那么我们如何在这里应用呢?
如果我们把第16行仅仅当作一个注解,那么在
main
方法中的这一点上,a2
的值将不再影响任何可能继续的计算,但是编译器/优化器/运行时可能无法推导出这一点,换句话说,第16行a2
的实际可达性状态是不清楚的。同样,除非应用程序中有一些超出我们所展示的代码之外的其他代码,否则
Alpha.b1
的值也不会影响任何潜在的继续计算。实际上,由于该变量只被写入,因此它 * 可以 * 被完全优化掉!但是,我们不知道编译器/优化器/运行时是否能够或将会推断出Alpha.b1
是不可访问的,尽管(IMO)他们这样做的可能性要小得多。还有几个额外的因素:
1.如果正在调试程序,这将影响优化器处理代码的方式,并可能影响可达性。例如,在
main
方法中设置断点可能会禁止任何幕后的a2
空值,因为程序员可能希望检查变量。1.这可能并不明显,但是可达性的确定 * 还 * 取决于
main
方法 * 是否已经过优化 *。对于使用HotSpot JIT编译器技术的当代JVM,它很可能不会在第一次(也是唯一一次)调用中得到优化。但是对于AOT编译器,它 * 可能 * 已经过优化。然后,第16行还有另一种解释,它是某个 * 实际 * 代码的占位符。在这种情况下,就
Alpha.b1
和a2
而言,所有的赌注都落空了。我们根本无法回答这个问题。总之,不可能给予关于仍有多少对象可达的明确答案。
注意,代码到了第16行,可达性是没有实际意义的。我想不出有什么会导致JVM需要在那个时候进行GC......或者在JVM进程退出之前的任何时候(没有shutdown钩子,并且不再支持“finalization on exit”)。
bn31dyow2#
让我们给予对象分配明确的名称,这样我们就不会与变量和字段名称混淆。
在第12行,我们将betaOne赋给一个静态变量,这意味着除非我们清除这个静态变量,否则betaOne将不适合垃圾收集。
在第14行,我们将betaTwo赋值给alphaTwo中的一个非静态变量,但是,当我们到达第16行时,我们没有清除alphaTwo,所以它的赋值变量仍然是可达的,所以betaTwo也仍然是可达的。
所以beta1是可达的,alpha 2和beta2是可达的,这就只剩下alpha 1是不可达的。
所以答案是:1.
iyr7buue3#
如果一个对象是不可达的,那么它就符合垃圾收集的条件。JLS,§12.6.1定义了不可达性:
每个对象都可以由两个属性表征:它可以是可达的、终结器可达的或不可达的,并且它也可以是未终结的、可终结的或已终结的。
可达对象是可以在任何潜在的连续计算中从任何活动线程访问的任何对象。
终结器可访问的对象可以从某个可终结的对象通过某个引用链访问,但不能从任何活动线程访问。
无法访问的对象不能通过任何一种方式访问。
看看手头的程序,我们看到最初创建了四个对象:
a1
、a2
、b1
和b2
(第10和11行);我们设置
a1.b1 = b1;
(第12行),这是一个用来愚弄无知者的行:我们通过示例引用设置static字段Alpha.a1
。然后,我们设置
a1.b2 = b1;
(第13行),注意我们现在设置了a1
的instance字段b2
。在第14行,我们设置了
a2.b2 = b2
,再次注意,我们设置了a2
的instance字段b2
。最后,我们
null
除a2
之外的所有引用。对于下面的分析,我将在assume (
urbandictionary.com
)中指出,第16行的注解是一个占位符,用于访问所有活动变量的计算(如果我们不做这个假设,那么根据JLS 12.6.1节,未使用变量引用的对象是不可访问的)。现在分析可达性:
a2
仍然可访问,因此此对象不适合进行垃圾回收。a2
,我们仍然可以访问a2.b2
,因此局部变量b2
以前引用的对象不合格Alpha
,我们仍然可以访问以前由b1
(Alpha.b1
)引用的对象,因此这个示例也不合格a1
引用的对象。因此,最后的答案是:一个对象适合于垃圾收集。
我们甚至可以通过
PhantomReference
s观察到1这种行为:Ideone.com
demo其中打印:
显示仅收集了
a1
。上面代码的注解:
在Java 16+中,我们会使用
a1.refersTo(null)
而不是a1.isEnqueued()
,因为方法isEnqueued()
已被弃用,并且添加了方法refersTo(T)
。1:程序依赖于垃圾收集器收集所有合格的对象,但这并不保证。垃圾收集器可能根本不运行,或者它可能没有收集所有合格的对象。此演示仅用于说明行为,而不是作为最终证据。有关证据,请阅读分析。
mo49yndu4#
在下面的问题中,当到达第16行时,有多少对象适合进行垃圾收集-----〉2