我以为我要对转义分析(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发生,因为在第一种情况下,您并没有调用包含分配的方法(在未知但数量较小的情况下)调用)。因此,这两个示例并不等效,因此无法得出有关逸出分析的结论。