我以为我要对转义分析(Java 8,64位服务器JVM)做一些实验。我想到了这个非常愚蠢的“应用程序”,在其中创建了许多Address对象(它们由邮政编码,街道,国家和生成该对象的时间戳组成。此外,Address具有isOk()方法,该方法返回如果时间戳可以用7除...则返回true。

所以这是程序:

private boolean generate() {
    boolean valid = true;
    for (int i=0;i<1_000_000_000;i++) {
        valid = valid && doGenerate();
    }

    return valid;
}

private boolean doGenerate() {
    long timeGenerated = System.currentTimeMillis();
    Address address = new Address(1021, "A Street", "A country", timeGenerated);
    return address.isOk();
}


到目前为止,到目前为止,我已经使用jVisualVM对它进行了概要分析,当堆运行时,堆上没有Address对象。整个应用程序将在几秒钟内完成。

但是,当我像这样重构它时:

private boolean generate() {
    boolean valid = true;
    for (int i=0;i<1_000_000_000;i++) {
        long timeGenerated = System.currentTimeMillis();
        Address address = new Address(1021, "A Street", "A country", timeGenerated);
        valid = valid && address.isOk();
    }

    return valid;
}


Baaang,没有转义分析,每个Address对象最终分配给堆的垃圾回收周期繁重。为什么会这样呢?我的意思是,Address实例不会以任何一种方式进行转义(在第二个版本中,Address对象的范围更窄,它们甚至都没有转义方法,甚至也没有for循环块),那么为什么两个版本的行为如此不同?

最佳答案

您编写“如果时间戳可以被7整除则返回true”。那应该很明显,会发生什么。在您的第一个代码中:

boolean valid = true;
for (int i=0;i<1_000_000_000;i++) {
    valid = valid && doGenerate();
}
return valid;


一旦时间戳不能被valid整除,false将变为7。然后,根据&&的工作方式,它将永远保持为false,并且由于&&处于短路状态,因此带有分配的方法doGenerate()将永远不会再被调用。

相比之下,在第二个变体中

boolean valid = true;
for (int i=0;i<1_000_000_000;i++) {
    long timeGenerated = System.currentTimeMillis();
    Address address = new Address(1021, "A Street", "A country", timeGenerated);
    valid = valid && address.isOk();
}
return valid;


一旦时间戳不能被valid整除,false也将变为并保持为7,但唯一短路的是调用isOk()。无论valid的值如何,都会进行构造。

原则上,这里可以省去Address的构造,但这将需要堆栈上的替换,因为它必须在循环运行时发生。目前尚不清楚这是否是问题所在,但更重要的结论是,在这两种情况下,我们都不会看到EA发生,因为在第一种情况下,您并没有调用包含分配的方法(在未知但数量较小的情况下)调用)。

因此,这两个示例并不等效,因此无法得出有关逸出分析的结论。

09-10 01:22
查看更多