1

我做了和这篇文章一样的测试: 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,我的老板让我做测试,在我展示测试结果后,他让我找到答案,为什么会发生这种情况。我在谷歌或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

谢谢!

4

1 回答 1

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分配实例。

于 2014-02-07T12:57:13.383 回答