java - 为什么 static final 在每次迭代中都比 new 慢

标签 java performance static

为什么代码片段 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/

相关文章:

Java:静态初始化

c# - C# 中相同函数的静态和非静态版本

java - 当用户在 JTextField 中键入时用数字替换字母

java - 如何从安卓日历中获取日期?

java - Eclipse - 像 Netbeans 一样编辑 web.xml (GUI)

php - 什么是 PHP 字符串是否以另一个字符串结尾的最有效测试?

laravel - View Composer 使用通配符 * 在所有 View 中加载变量 laravel 5

sql - 衡量 SQL 查询的性能

java - 不常用的 Java 语法 (JavaParser)?

java - 抽象类或接口(interface)中的 public static final 字段