【问题标题】:Java String = "" vs. new String("") performance changeJava String = "" vs. new String("") 性能变化
【发布时间】:2014-02-07 22:50:41
【问题描述】:

我做了和这篇文章一样的测试: 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

谢谢!

【问题讨论】:

  • 我强烈怀疑您只是看到 Hotspot 随着时间的推移更加优化。 “百万次重复”部分可能显示了其他一些因素......它真的不会更快。
  • 查找 Hotspot 并牢记垃圾收集,以回答为什么您会看到执行次数较多时的差异。对于 Java 中的未来分配,很快就会忘记这种级别的优化。正如卡亚曼所说——担心这是浪费时间。衡量有意义的算法,但不要让这个结果影响你编写任何代码的方式!

标签: java string performance microbenchmark


【解决方案1】:

首先,您必须了解很多有关 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 实例。

【讨论】:

  • 感谢您的快速回复
  • 有一点需要注意的是,当你不应该在这些测试上浪费时间时;您不会智取 JVM,它在过去十年中已被微调为性能优化野兽。您实际执行测试从而得到统计谎言的机会已经太大了。
  • @Gimby:基本同意,但在某些情况下,您可以做得比简单地依靠 JVM 为您完成所有工作更好。但是很少见,它需要相当多的知识、分析和时间。有意义的情况非常罕见。除非你这样做是为了好玩;)
  • 实际上你应该依赖它,直到它不依赖它的那一刻。然后你开始在问题上投入时间。在您测量一个之前没有问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
  • 2021-04-04
  • 2020-11-13
相关资源
最近更新 更多