我做了同样的测试,就像在这篇文章中所做的那样: new String() vs literal string performance
意思是我想测试哪一个性能更好。正如我所料,结果是文字的分配更快。我不知道为什么,但我做了一些更多的任务测试,我注意到一些奇怪的事情:当我让程序执行循环超过10.000次时,文字的分配相对没有快于少于10.000分配的分配。并且在重复1.000.000时,它甚至比创建新对象更慢。
这是我的代码:
double tx = System.nanoTime();
for (int i = 0; i<1; i++){
String s = "test";
}
double ty = System.nanoTime();
double ta = System.nanoTime();
for (int i = 0; i<1; i++){
String s = new String("test");
}
double tb = System.nanoTime();
System.out.println((ty-tx));
System.out.println((tb-ta));
我让它像上面所写的那样运行。我只是在学习Java,而我的老板让我做测试,在我提出测试结果后,他让我找到答案,为什么会这样。我在google或stackoverflow中找不到任何内容,所以我希望有人可以帮助我。
factor at 1 repetition 3,811565221
factor at 10 repetitions 4,393570401
factor at 100 repetitions 5,234779103
factor at 1,000 repetitions 7,909884116
factor at 10,000 repetitions 9,395538811
factor at 100,000 repetitions 2,355514697
factor at 1,000,000 repetitions 0,734826755
谢谢!
答案 0 :(得分:10)
首先,你必须学习很多关于HotSpot的内部结构,特别是你的代码首先被解释,然后在某个点编译成本机代码。
根据代码的静态和动态分析结果,在编译时会发生很多优化。
具体来说,在您的代码中,
String s = "test";
是明确的无操作。编译器将为此行发出无代码。剩下的就是循环本身,如果HotSpot证明它没有可观察到的外部影响,整个循环可能会被消除。
其次,甚至是代码
String s = new String("test");
可能会产生与上面几乎相同的东西,因为很容易证明你的new String
是一个无法从创建它的方法中逃脱的实例。
使用您的代码,测量结果会混合解释字节码的性能,编译代码所需的延迟以及通过堆栈替换进行交换,然后是本机代码的性能。
基本上,您正在进行的测量是测量所有内容,但您要测量的效果。
为了使参数更加可靠,我用jmh
重复了测试:
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 1, time = 1)
@Measurement(iterations = 3, time = 1)
@Threads(1)
@Fork(2)
public class Strings
{
static final int ITERS = 1_000;
@GenerateMicroBenchmark
public void literal() {
for (int i = 0; i < ITERS; i++) { String s = "test"; }
}
@GenerateMicroBenchmark
public void newString() {
for (int i = 0; i < ITERS; i++) { String s = new String("test"); }
}
}
这些是结果:
Benchmark Mode Samples Mean Mean error Units
literal avgt 6 0.625 0.023 ns/op
newString avgt 6 43.778 3.283 ns/op
你可以看到在字符串文字的情况下整个方法体被消除了,而new String
循环仍然存在,但是没有任何东西,因为每个循环迭代的时间只有0.04纳秒。绝对没有分配String
个实例。