java - 为什么 new Something() 和 Something.class.newinstance() 之间没有明显的区别?

标签 java performance

我正在运行以下实验,并且惊讶地发现两次运行之间没有可测量的差异:

public static void main(String[] args) throws Exception {
    long count = 1_000_000_0L;
    long measureCount = 1000L;


    // Measurement 1
    SpecificRecord tp1 = null;
    List<Long> times = new ArrayList<>();
    for (long j=0; j<measureCount; ++j) {
        long timeStart = System.currentTimeMillis();
        for (long i = 0; i < count; ++i) {
            tp1 = new WebPageView();
        }
        times.add(System.currentTimeMillis() - timeStart);
    }
    Stats st = Stats.of(times);
    double avg = st.mean();
    double stdDev = st.populationStandardDeviation();
    times.sort(Long::compareTo);
    int upper = (int)Math.ceil(times.size()/2.0);
    int lower = (int)Math.floor(times.size()/2.0);
    double median = ((times.get(upper))+(times.get(lower)))/2.0;
    System.out.println("avg: "+avg);
    System.out.println("stdDev: "+stdDev);
    System.out.println("median: "+median);
    System.out.println(tp1);


    // Measurement 2
    SpecificRecord tp2 = null;
    List<Long> times2 = new ArrayList<>();
    for (long j=0; j<measureCount; ++j) {
        long timeStart = System.currentTimeMillis();
        for (long i = 0; i < count; ++i) {
            tp2 = WebPageView.class.newInstance();
        }
        times2.add(System.currentTimeMillis() - timeStart);
    }
    Stats st2 = Stats.of(times2);
    double avg2 = st2.mean();
    double stdDev2 = st2.populationStandardDeviation();
    times2.sort(Long::compareTo);
    int upper2 = (int)Math.ceil(times2.size()/2.0);
    int lower2 = (int)Math.floor(times2.size()/2.0);
    double median2 = ((times2.get(upper2))+(times2.get(lower2)))/2.0;
    System.out.println("avg: "+avg2);
    System.out.println("stdDev: "+stdDev2);
    System.out.println("median: "+median2);
    System.out.println(tp2);
  }
}

结果:

avg: 110.63300000000005
stdDev: 47.07256431298379
median: 100.0
{"aid": 0, "uid": 0, "rid": 0, "sid": 0, "d": null, "p": null, "r": null, "f": null, "q": null, "ts": 0}
avg: 101.0840000000001
stdDev: 7.8092857547921835
median: 99.0
{"aid": 0, "uid": 0, "rid": 0, "sid": 0, "d": null, "p": null, "r": null, "f": null, "q": null, "ts": 0}

更新 1:

你们中的许多人指出,由于大量优化伪装了 new Something() 和 Something.class.newinstance() 之间的性能差异,因此不可能以这种方式对 JVM 进行基准测试。

更新2:

用建议的方法重复测试后,结果让我有点惊讶:

Benchmark                   Mode  Cnt   Score   Error  Units
ReflectionTest.newInstance  avgt    5  12.923 ± 0.801  ns/op
ReflectionTest.newOperator  avgt    5  11.524 ± 0.289  ns/op

更新3: 测试代码:

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class ReflectionTest {

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder().include(ReflectionTest.class.getSimpleName()).forks(1).build();
        new Runner(opt).run();
    }

    @Benchmark
    public WebPageView newOperator() {
        return new WebPageView();
    }

    @Benchmark
    public WebPageView newInstance() throws InstantiationException, IllegalAccessException {
        return WebPageView.class.newInstance();
    }
}

这个问题仍然没有答案,没有测试可以概述我们使用的类的 class.newInstance() 与 new 之间的任何区别。此类使用的是 Avro SpecificRecord 的实现。

public class WebPageView extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord

最佳答案

编辑

测试仍然很好,但它是用 jdk-9 运行的,事实证明它比 jdk-8 慢得多。

使用 jdk-8 运行,将证明 JIT 进行了大量优化,真正的差异在 2x 左右。

使用 jmh 进行的一些测试显示出很大的不同(使用 jdk-9 运行)

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class TestNewObject {

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder().include(TestNewObject.class.getSimpleName()).forks(1).build();
        new Runner(opt).run();
    }

    @Benchmark
    public Something newOperator() {
        return new Something();
    }

    @Benchmark
    public Something newInstance() throws InstantiationException, IllegalAccessException {
        return Something.class.newInstance();
    }

    static class Something {

    }
}

结果,显示出相当大的差异。

Benchmark                  Mode  Cnt    Score    Error  Units
TestNewObject.newInstance  avgt    5  274.070 ± 50.554  ns/op
TestNewObject.newOperator  avgt    5    5.119 ±  3.550  ns/op

这是一个 44x 的速度差异。对 newInstance 的调用做了很多(检查源代码)非常昂贵的事情(反射和安全检查)。

虽然 new 运算符,但考虑到 TLAB 是一种非常快速的分配对象的方法。

需要提及的是 newInstance 在 jdk-9 中已被弃用。

关于java - 为什么 new Something() 和 Something.class.newinstance() 之间没有明显的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42771964/

相关文章:

java - 从 ARRAYLIST WITHOUT LOOP 中选择对象 - ANDROID

Java Swing : Forcing certain items to the end of a sorted JTable

java - 访问 neo4j-ogm 的映射上下文

java - 通过调用 DAO.getAll() 避免收集大量 ID

c# - WebClient 下载速度非常慢

java - 合并 2 个列表并在匹配后设置值

java - 无法连接到 SMTP 主机 : {remote-server}, 端口 : {port}, 响应:-1

java - 记录 Zulu 代理请求的响应时间

django - 如何提高这个 django ORM 查询的性能?

javascript - 为什么在此实例中使用 `concat` 而不是 `push`?