为什么静态final在每次迭代时都比new更慢

时间:2016-06-23 18:11:47

标签: java performance static

为什么代码段比代码段B慢14倍? (在Windows 7 64位上使用jdk1.8.0_60进行测试)

代码段A:

blankBitmapRawData

代码段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 += RECTANGLE.hashCode();            // <= Only change is on this line
        }
        System.out.println((System.nanoTime() - start) / 1_000_000);
        System.out.println(result);
    }
}

TL; DR:在循环中使用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); } } 关键字比访问new字段更快。

(注意:删除static final上的final关键字不会改变执行时间)

1 个答案:

答案 0 :(得分:15)

在第一种情况下(静态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与返回常量一样快,因为在JIT编译期间消除了对象分配并且hashCode是常量折叠的。

禁用EliminateAllocations优化时,基准时间明显更高,但hashCode的算术运算仍然是不变的。

在上一个基准测试中,即使RECTANGLE被声明为final,但其字段可能在理论上已更改,因此JIT无法消除字段访问。