有人知道为什么在Java 8和Java 11上运行此代码时会得到如此不同的性能吗?
在不使用任何运行时标志的情况下,与Java 8相比,此代码在Java 11下的运行速度明显慢。
import java.util.Date;
public class PerformanceExperiment {
public static volatile String s = "";
public static void main(String[] args)
{
System.out.println("Starting performance test");
String s1 = "STRING ONE";
String s2 = "STRING TWO";
long now1 = (new Date()).getTime();
for (long i = 0; i < 1_000_000_00; i++)
{
s = "abc " + s1 + " def " + s2;
}
long now2 = (new Date()).getTime();
System.out.println("initial block took " + (now2 - now1) + "ms");
for (long i = 0; i < 4_000_000_00; i++)
{
s = "abc " + s1 + " def " + s2;
}
long now3 = (new Date()).getTime();
System.out.println("Main block took " + (now3 - now2) + "ms");
}
}
我尝试了许多命令行标志,但没有找到与Java 8性能匹配的任何东西。
我只在Windows上进行过测试,因此它在其他操作系统上的表现可能会有所不同。
答案 0 :(得分:6)
我将您的应用修改为
System.nanoTime()
代替new Date()
可以提高精度(有关更多信息,请参见此答案:https://stackoverflow.com/a/1776053/963076)。在JDK 8 v181中使用Netbeans 8.2:
Starting performance test 0
initial block took 3147ms
Main block took 9469ms
Starting performance test 1
initial block took 2398ms
Main block took 9601ms
Starting performance test 2
initial block took 2463ms
Main block took 9671ms
Starting performance test 3
initial block took 2464ms
Main block took 9565ms
Starting performance test 4
initial block took 2410ms
Main block took 9672ms
Starting performance test 5
initial block took 2418ms
Main block took 9598ms
Starting performance test 6
initial block took 2384ms
Main block took 9733ms
Starting performance test 7
initial block took 2402ms
Main block took 9610ms
Starting performance test 8
initial block took 2509ms
Main block took 11222ms
Starting performance test 9
initial block took 2455ms
Main block took 10661ms
分析器显示了此遥测:
使用JDK 11.0.2的Netbeans 10.0:
Starting performance test 0
initial block took 3760ms
Main block took 15056ms
Starting performance test 1
initial block took 3734ms
Main block took 14602ms
Starting performance test 2
initial block took 3615ms
Main block took 14762ms
Starting performance test 3
initial block took 3748ms
Main block took 14534ms
Starting performance test 4
initial block took 3628ms
Main block took 14759ms
Starting performance test 5
initial block took 3625ms
Main block took 14959ms
Starting performance test 6
initial block took 3987ms
Main block took 14967ms
Starting performance test 7
initial block took 3803ms
Main block took 14701ms
Starting performance test 8
initial block took 3599ms
Main block took 14762ms
Starting performance test 9
initial block took 3627ms
Main block took 14434ms
我的结论:为了提高内存效率,JDK 11正在做更多的工作。注意,使用JDK11时,垃圾收集器中“存活的世代”的数量减少了(方式),并且在使用率和易变性方面,内存使用量也显着减少。权衡似乎在速度上,但是速度的差异小于内存使用的差异。
答案 1 :(得分:2)
TL; DR:需要更好的基准测试,更好的设置以控制版本之间的差异等。使用JMH可以轻松解决大多数基准测试问题。当前的测试行为似乎是通过可疑的基准测试方法和默认GC的更改来解释的。
考虑一下:
public class PerformanceExperiment {
public static volatile String s = "";
public static void main(String[] args) {
for (int c = 0; c < 5; c++) {
test();
}
}
public static void test() {
String s1 = "STRING ONE";
String s2 = "STRING TWO";
long time1 = System.currentTimeMillis();
for (long i = 0; i < 4_000_000_00; i++) {
s = "abc " + s1 + " def " + s2;
}
long time2 = System.currentTimeMillis();
System.out.println("Main block took " + (time2 - time1) + "ms");
}
}
首先,它使用更方便的计时。然后,它测量相同的字节码块,而原始测试会预热“初始1”,然后继续测量绝对冷的那个。
然后,JIT编译将调用该方法,并且您想再次重新输入该方法以使优化的代码运行,否则,您将运行中间的“ on-stack-replacement”代码-您可以使用外部调用test
的迭代。最重要的是,您想要多次输入以捕获最优化的版本。
而且,由于测试分配了很多内存,因此您希望确定堆大小。
所以,这里:
$ ~/Install/jdk8u191-rh/bin/javac PerformanceExperiment.java
$ ~/Install/jdk8u191-rh/bin/java -Xms2g -Xmx2g PerformanceExperiment
Main block took 10024ms
Main block took 9768ms
Main block took 7249ms
Main block took 7235ms
Main block took 7205ms
...这是同一字节码上的11.0.2:
$ ~/Install/jdk11.0.2/bin/java -Xms2g -Xmx2g PerformanceExperiment
Main block took 9775ms
Main block took 10825ms
Main block took 8635ms
Main block took 8616ms
Main block took 8622ms
...这是具有匹配GC的11.0.2(9+使用JEP 248将默认值更改为G1):
$ ~/Install/jdk11.0.2/bin/java -Xms2g -Xmx2g -XX:+UseParallelGC PerformanceExperiment
Main block took 9281ms
Main block took 9129ms
Main block took 6725ms
Main block took 6688ms
Main block took 6684ms
最重要的是,每个小迭代中都有volatile
个存储,这会花费很多,并且可能会扭曲基准。
还有interaction个带有字符串concat (JEP 280),线程本地握手(JEP 312)和其他VM修复程序,但您可能只有在编译超过target = 8时才能看到,这超出了本练习的范围。
答案 2 :(得分:0)
现在已确认这是JDK中的错误:
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8221733