我以为我会做一些逃避分析的实验(Java 8,64位服务器JVM)。我提出了这个非常愚蠢的应用程序",我创建了很多地址对象(它们包含邮政编码,街道,国家和生成对象的时间戳。此外,地址有一个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对象&#39;范围甚至更窄,它们不会逃避该方法,甚至不是for循环块),为什么这两个版本表现得如此不同?
答案 0 :(得分:4)
你写“如果时间戳可用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
,但唯一被短路的是调用{ {1}}。无论isOk()
的值如何,都会发生构造。
原则上,valid
的构造可以在这里消除,但这需要堆栈替换,因为它必须在循环运行时发生。目前尚不清楚这是否是问题,但更重要的结论是,在既不的情况下,我们看到EA发生,就像在第一种情况下,你没有调用包含分配的方法(之后)一个未知的,但预计会有少量的调用)。
因此,这两个例子并不等同,不允许得出关于逃逸分析的结论。