逃避分析疑惑

Escape analysis doubts

我想我会做一个逃逸分析的小实验(Java 8、64 位服务器 JVM)。我想出了这个非常愚蠢的 "application",我在其中创建了很多 Address 对象(它们由邮政编码、街道、国家/地区和对象生成的时间戳组成。此外,Address 有一个 isOk() 方法,如果时间戳可以被 7 整除,则 returns 为真...)。

程序如下:

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 对其进行了概要分析,堆上没有地址对象,而它是 运行。整个应用程序在几秒钟内完成。

然而,当我这样重构它时:

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 整除则 returns 为真”。这应该很明显,会发生什么。在您的第一个代码中:

boolean valid = true;
for (int i=0;i<1_000_000_000;i++) {
    valid = valid && doGenerate();
}
return valid;
一旦时间戳碰巧不能被 7 整除,

valid 将变为 false。那么,根据&&的工作方式,它会永远保持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;
一旦时间戳碰巧不能被 7 整除,

valid 也会变成并保持 false,但唯一短路的是调用isOk()。无论 valid.

的值如何,构造都会发生

原则上,Address 的构造可以在此处消除,但这需要栈上替换,因为它必须在循环运行时发生。目前尚不清楚这是否是这里的问题,但更重要的结论是,在 两种 情况下,我们都看到 EA 发生了,因为在第一种情况下,您没有调用包含分配(在未知但预期的少量调用之后)。

所以这两个例子是不等价的,不能得出逃逸分析的结论。