我正在尝试一些有关字符串池的性能基准。然而,结果并不令人期待。
我做了3个静态方法
- Perform0() 方法...每次都会创建一个新对象
- Perform1() 方法...字符串文字“Test”
- Perform2()方法...字符串常量表达式“Te”+“st”
我的期望是(1.最快 -> 3.最慢)
- 由于字符串池而“测试”
- “Te”+“st”,因为字符串池,但比 1 慢一点,因为 + 运算符
- new String(..) 因为没有字符串池。
但基准测试显示“测试”比“测试”稍快。
new String(): 141677000 ns
"Test" : 1148000 ns
"Te"+"st" : 1059000 ns
new String(): 141253000 ns
"Test" : 1177000 ns
"Te"+"st" : 1089000 ns
new String(): 142307000 ns
"Test" : 1878000 ns
"Te"+"st" : 1082000 ns
new String(): 142127000 ns
"Test" : 1155000 ns
"Te"+"st" : 1078000 ns
...
这是代码:
import java.util.concurrent.TimeUnit;
public class StringPoolPerformance {
public static long perform0() {
long start = System.nanoTime();
for (int i=0; i<1000000; i++) {
String str = new String("Test");
}
return System.nanoTime()-start;
}
public static long perform1() {
long start = System.nanoTime();
for (int i=0; i<1000000; i++) {
String str = "Test";
}
return System.nanoTime()-start;
}
public static long perform2() {
long start = System.nanoTime();
for (int i=0; i<1000000; i++) {
String str = "Te"+"st";
}
return System.nanoTime()-start;
}
public static void main(String[] args) {
long time0=0, time1=0, time2=0;
for (int i=0; i<100; i++) {
// result
time0 += perform0();
time1 += perform1();
time2 += perform2();
}
System.out.println("new String(): " + time0 + " ns");
System.out.println("\"Test\" : " + time1 + " ns");
System.out.println("\"Te\"+\"st\" : " + time2 + " ns");
}
}
有人可以解释为什么“Te”+“st”比“Test”执行得更快吗? JVM在这里做了一些优化吗?谢谢。
"Te" + "st"
是编译器时常量表达式,因此将在运行时表现没有什么不同而不是简单地"Test"
。任何性能影响都将发生在尝试编译它时,而不是尝试运行它时。
通过使用反汇编编译的基准类可以轻松证明这一点javap -c StringPoolPerformance
:
public static long perform1();
Code:
...
7: ldc #3; //int 1000000
9: if_icmpge 21
12: ldc #5; //String Test
14: astore_3
15: iinc 2, 1
...
public static long perform2();
Code:
...
7: ldc #3; //int 1000000
9: if_icmpge 21
12: ldc #5; //String Test
14: astore_3
15: iinc 2, 1
...
这些方法的字节码完全相同!这是由Java 语言规范,15.18.1 http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.18.1:
除非表达式是编译时常量表达式(第 15.28 节),否则 String 对象是新创建的(第 12.5 节)。
您遇到的基准差异可能是由于典型的可变性或因为您的基准并不完美。看这个问题:如何用 Java 编写正确的微基准测试? https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java
您违反的一些值得注意的规则:
- 您不会丢弃测试内核“预热”迭代的结果。
- 您没有启用 GC 日志记录(特别是当
perform1()
is always在创建一百万个对象的测试之后立即运行)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)