为什么代码片段 A 比代码片段 B 慢 14 倍?
(在 Windows 7 64 位上使用 jdk1.8.0_60 测试)
代码片段 A:
import java.awt.geom.RoundRectangle2D;
public class Test {
private static final RoundRectangle2D.Double RECTANGLE = new RoundRectangle2D.Double(1, 2, 3, 4, 5, 6);
public static void main(String[] args) {
int result = RECTANGLE.hashCode();
long start = System.nanoTime();
for (int i = 0; i < 100_000_000; i++) {
result += RECTANGLE.hashCode(); // <= Only change is on this line
}
System.out.println((System.nanoTime() - start) / 1_000_000);
System.out.println(result);
}
}
代码片段 B:
import java.awt.geom.RoundRectangle2D;
public class Test {
private static final RoundRectangle2D.Double RECTANGLE = new RoundRectangle2D.Double(1, 2, 3, 4, 5, 6);
public static void main(String[] args) {
int result = RECTANGLE.hashCode();
long start = System.nanoTime();
for (int i = 0; i < 100_000_000; i++) {
result += new RoundRectangle2D.Double(1, 2, 3, 4, 5, 6).hashCode();
}
System.out.println((System.nanoTime() - start) / 1_000_000);
System.out.println(result);
}
}
TL;DR:在循环中使用 new
关键字比访问 static final
字段更快。
(注意:删除 RECTANGLE
上的 final
关键字不会改变执行时间)
最佳答案
第一种情况(static final)JVM需要从内存中读取对象字段。 在第二种情况下,已知值是恒定的。此外,由于对象没有从循环中逃脱,分配被消除,例如它的字段被替换为局部变量。
以下JMH基准支持理论:
package bench;
import org.openjdk.jmh.annotations.*;
import java.awt.geom.RoundRectangle2D;
@State(Scope.Benchmark)
public class StaticRect {
private static final RoundRectangle2D.Double RECTANGLE =
new RoundRectangle2D.Double(1, 2, 3, 4, 5, 6);
@Benchmark
public long baseline() {
return 0;
}
@Benchmark
public long testNew() {
return new RoundRectangle2D.Double(1, 2, 3, 4, 5, 6).hashCode();
}
@Benchmark
@Fork(jvmArgs = "-XX:-EliminateAllocations")
public long testNewNoEliminate() {
return new RoundRectangle2D.Double(1, 2, 3, 4, 5, 6).hashCode();
}
@Benchmark
public int testStatic() {
return RECTANGLE.hashCode();
}
}
结果:
Benchmark Mode Cnt Score Error Units
StaticRect.baseline avgt 10 2,840 ± 0,048 ns/op
StaticRect.testNew avgt 10 2,831 ± 0,011 ns/op
StaticRect.testNewNoEliminate avgt 10 8,566 ± 0,036 ns/op
StaticRect.testStatic avgt 10 12,689 ± 0,057 ns/op
testNew
与返回常量一样快,因为消除了对象分配并且 hashCode
在 JIT 编译期间被常量折叠。
当禁用EliminateAllocations
优化时,基准测试时间明显更长,但hashCode
的算术计算仍然是常量折叠。
在上一个基准测试中,尽管 RECTANGLE
被声明为 final,但理论上它的字段可以更改,因此 JIT 无法消除字段访问。
关于java - 为什么 static final 在每次迭代中都比 new 慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37998919/