Java 为什么每次迭代静态final都比新的慢
为什么代码段A比代码段B慢14倍?Java 为什么每次迭代静态final都比新的慢,java,performance,static,Java,Performance,Static,为什么代码段A比代码段B慢14倍? (在Windows 7 64位上使用jdk1.8.0_60进行测试) 代码段A: 导入java.awt.geom.RoundRectangle2D; 公开课考试{ 私有静态最终RoundRectangle2D.Double矩形=新的RoundRectangle2D.Double(1,2,3,4,5,6); 公共静态void main(字符串[]args){ int result=RECTANGLE.hashCode(); 长启动=System.nanoTime
(在Windows 7 64位上使用jdk1.8.0_60进行测试) 代码段A:
导入java.awt.geom.RoundRectangle2D;
公开课考试{
私有静态最终RoundRectangle2D.Double矩形=新的RoundRectangle2D.Double(1,2,3,4,5,6);
公共静态void main(字符串[]args){
int result=RECTANGLE.hashCode();
长启动=System.nanoTime();
对于(int i=0;i<100_000;i++){
result+=RECTANGLE.hashCode();//在第一种情况下(静态final),JVM需要从内存中读取对象字段。
在第二种情况下,已知值是常量。此外,由于对象没有从循环中逃逸,因此消除了分配,例如,它的字段被局部变量替换
以下基准支持该理论:
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
与返回常量一样快,因为对象分配被消除,并且在JIT编译期间,hashCode
是常量折叠的
当禁用EliminateAllocations
优化时,基准时间明显较高,但hashCode
的算术计算仍然是常数折叠的
在上一个基准测试中,即使RECTANGLE
声明为final,其字段在理论上可能会更改,因此JIT无法消除字段访问。您的测试没有考虑JVM预热时间/启动时间。您的结果可能与编写的结果不一致。您实际上是在测试JVM启动的速度,然后执行您的代码。@SnakeDoc,JVM预热/启动当然是一个考虑因素,但它并不能解释我看到的OP代码的性能差异。即使在将预热循环插入较慢的代码中之后,也会(改进)性能并没有接近更快的性能。不,我尝试在一个应用程序中运行案例A,然后运行案例B,然后颠倒顺序,在这两种情况下,案例A都是21xlonger@qwertzguy如果它真的完全相同,那么就没有区别了。字节码中有些不同,也许你只是缺少了它leThreshold=0,结果正如我们所期望的,A比B快。它的JIT优化魔法。通过常数折叠,您的意思是将各种RoundRectangle2D.Double
字段提取到局部变量中,然后对这些变量计算hashCode
,最终发现它总是返回相同的值?大概是y您可以通过将其中一个构造函数参数更改为计算变量来打破这一切(在本例中,计算结果为2)?所以在上一个基准测试中,它无法优化的唯一原因是对矩形字段的更改可以由另一个线程完成?@SotiriosDelimanolis是的,JIT意识到表达式的所有操作数都是常量,因此表达式也是常量。如果添加变量,表达式将在运行时计算,但这将不一定需要内存加载。@qwertzguy矩形对象不在本地上下文中;HotSpot通常不会消除可能从本地作用域逃逸的对象的字段访问。虽然这不是绝对不可能的,但这样的优化相当困难,通常不值得。总之,这意味着将对象设置为静态final而不是每次创建一个新实例可能会比较慢(当构造函数很快时),这使得很难有一个定义良好的最佳实践。
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