java 在下面的问题中,当到达第16行时,有多少对象适合进行垃圾收集

flmtquvp  于 2023-01-15  发布在  Java
关注(0)|答案(4)|浏览(106)
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个对象可用于垃圾回收

8zzbczxx

8zzbczxx1#

答案取决于Java实现...以及你选择如何解释这个问题。特别是第16行到底是什么意思?

  • 如果我们按照Java编译器的解释来解释第16行,那么它只是一个注解,什么也不做。
  • 另一种解释是,第16行是一些 * 真实的 * Java代码的编辑“速记”,这些代码可能使用也可能不使用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.b1a2而言,所有的赌注都落空了。我们根本无法回答这个问题。
总之,不可能给予关于仍有多少对象可达的明确答案。
注意,代码到了第16行,可达性是没有实际意义的。我想不出有什么会导致JVM需要在那个时候进行GC......或者在JVM进程退出之前的任何时候(没有shutdown钩子,并且不再支持“finalization on exit”)。

bn31dyow

bn31dyow2#

让我们给予对象分配明确的名称,这样我们就不会与变量和字段名称混淆。

  • 第一个分配的贝塔-贝塔一
  • 第二个分配的贝塔-贝塔二
  • 第一个分配的阿尔法-阿尔法一
  • 第二个分配了α-α 2.

在第12行,我们将betaOne赋给一个静态变量,这意味着除非我们清除这个静态变量,否则betaOne将不适合垃圾收集。
在第14行,我们将betaTwo赋值给alphaTwo中的一个非静态变量,但是,当我们到达第16行时,我们没有清除alphaTwo,所以它的赋值变量仍然是可达的,所以betaTwo也仍然是可达的。
所以beta1是可达的,alpha 2和beta2是可达的,这就只剩下alpha 1是不可达的。
所以答案是:1.

iyr7buue

iyr7buue3#

如果一个对象是不可达的,那么它就符合垃圾收集的条件。JLS,§12.6.1定义了不可达性:
每个对象都可以由两个属性表征:它可以是可达的、终结器可达的或不可达的,并且它也可以是未终结的、可终结的或已终结的。
可达对象是可以在任何潜在的连续计算中从任何活动线程访问的任何对象。
终结器可访问的对象可以从某个可终结的对象通过某个引用链访问,但不能从任何活动线程访问。
无法访问的对象不能通过任何一种方式访问。
看看手头的程序,我们看到最初创建了四个对象:a1a2b1b2(第10和11行);
我们设置a1.b1 = b1;(第12行),这是一个用来愚弄无知者的行:我们通过示例引用设置static字段Alpha.a1
然后,我们设置a1.b2 = b1;(第13行),注意我们现在设置了a1instance字段b2
在第14行,我们设置了a2.b2 = b2,再次注意,我们设置了a2instance字段b2
最后,我们nulla2之外的所有引用。
对于下面的分析,我将在assume ( urbandictionary.com )中指出,第16行的注解是一个占位符,用于访问所有活动变量的计算(如果我们不做这个假设,那么根据JLS 12.6.1节,未使用变量引用的对象是不可访问的)。
现在分析可达性:

  • a2仍然可访问,因此此对象不适合进行垃圾回收。
  • 通过a2,我们仍然可以访问a2.b2,因此局部变量b2以前引用的对象不合格
  • 通过类Alpha,我们仍然可以访问以前由b1Alpha.b1)引用的对象,因此这个示例也不合格
  • 唯一不再可访问的示例是以前由a1引用的对象。

因此,最后的答案是:一个对象适合于垃圾收集。
我们甚至可以通过PhantomReference s观察到1这种行为:

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.concurrent.TimeUnit;

class Ideone {
  public static void main(String[] args) throws InterruptedException {
    Beta b1 = new Beta();
    Beta b2 = new Beta();
    Alpha a1 = new Alpha();
    Alpha a2 = new Alpha();
    a1.b1 = b1;
    a1.b2 = b1;
    a2.b2 = b2;
    PhantomReference<Alpha> a1Phantom =
        new PhantomReference<>(a1, new ReferenceQueue<>());
    PhantomReference<Alpha> a2Phantom =
        new PhantomReference<>(a2, new ReferenceQueue<>());
    PhantomReference<Beta> b1Phantom =
        new PhantomReference<>(b1, new ReferenceQueue<>());
    PhantomReference<Beta> b2Phantom =
        new PhantomReference<>(b2, new ReferenceQueue<>());
    a1 = null;
    b1 = null;
    b2 = null;
    System.gc();
    Thread.sleep(TimeUnit.SECONDS.toMillis(1));
    System.out.printf(
        "a1 has %sbeen collected%n", 
        a1Phantom.isEnqueued() ? "" : "not ");
    System.out.printf(
        "a2 has %sbeen collected%n", 
        a2Phantom.isEnqueued() ? "" : "not ");
    System.out.printf(
        "b1 has %sbeen collected%n", 
        b1Phantom.isEnqueued() ? "" : "not ");
    System.out.printf(
        "b2 has %sbeen collected%n", 
        b2Phantom.isEnqueued() ? "" : "not ");
  }
}

class Beta {
}

class Alpha {
  static Beta b1;
  Beta b2;
}

Ideone.com demo
其中打印:

a1 has been collected
a2 has not been collected
b1 has not been collected
b2 has not been collected

显示仅收集了a1
上面代码的注解:
在Java 16+中,我们会使用a1.refersTo(null)而不是a1.isEnqueued(),因为方法isEnqueued()已被弃用,并且添加了方法refersTo(T)
1:程序依赖于垃圾收集器收集所有合格的对象,但这并不保证。垃圾收集器可能根本不运行,或者它可能没有收集所有合格的对象。此演示仅用于说明行为,而不是作为最终证据。有关证据,请阅读分析。

mo49yndu

mo49yndu4#

在下面的问题中,当到达第16行时,有多少对象适合进行垃圾收集-----〉2

相关问题