我正在运行以下实验,并且惊讶地发现两次运行之间没有可测量的差异:
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/