简体   繁体   English

for循环中的字符串连接。 Java 9

[英]String concatenation in a for loop. Java 9

Please correct me if i'm wrong. 如果我错了,请纠正我。 In Java 8, for performance reasons, when concatenating several strings by the "+" operator StringBuffer was invoked. 在Java 8中,出于性能原因,当通过“+”运算符连接多个字符串时,调用了StringBuffer。 And the problem of creating a bunch of intermediate string objects and polluting the string pool was "resolved". 并且“解决”了创建一堆中间字符串对象和污染字符串池的问题。

What about Java 9? Java 9怎么样? There'a new feature added as Invokedynamic. Invokedynamic增加了一项新功能。 And a new class that resolves the problem even better, StringConcatFactory. 还有一个新的类可以更好地解决问题,StringConcatFactory。

String result = "";
List<String> list = Arrays.asList("a", "b", "c");
for (String n : list) {
 result+=n;
}

My question are: How many objects are created in this loop? 我的问题是:在这个循环中创建了多少个对象? Are there any intermedier objects? 有中介对象吗? And how can i verify that? 我该如何验证呢?

For the record, here is a JMH test... 为了记录,这是一个JMH测试......

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
public class LoopTest {

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder().include(LoopTest.class.getSimpleName())
                .jvmArgs("-ea", "-Xms10000m", "-Xmx10000m")
                .shouldFailOnError(true)
                .build();
        new Runner(opt).run();
    }

    @Param(value = {"1000", "10000", "100000"})
    int howmany;

    @Fork(1)
    @Benchmark
    public String concatBuilder(){
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<howmany;++i){
            sb.append(i);
        }
        return sb.toString();
    }

    @Fork(1)
    @Benchmark
    public String concatPlain(){
        String result = "";
        for(int i=0;i<howmany;++i){
            result +=i;
        }
        return result;
    }
}

Produces result (only for 100000 shown here) that I did not really expect: 生成结果(仅显示此处显示的100000 ),我并不是真的期望:

LoopTest.concatPlain       100000  avgt    5  3902.711 ± 67.215  ms/op
LoopTest.concatBuilder     100000  avgt    5     1.850 ±  0.574  ms/op

My question is: How many objects are created in this loop? 我的问题是:在这个循环中创建了多少个对象? Are there any intermediate objects? 有中间物吗? How can I verify that? 我该如何验证?

Spoiler: 扰流板:

JVM doesn't try to omit intermediate objects in the loop - so they will be created when using plain concatenation. JVM不会尝试在循环中省略中间对象 - 因此在使用纯连接时将创建它们。

Let's take a look at the bytecode first. 我们先来看看字节码。 I used performance tests kindly provided by @Eugene, compiled them for java8 and then for java9. 我使用了@Eugene友情提供的性能测试,为java8编译,然后为java9编译。 Here are those 2 methods we gonna compare: 以下是我们要比较的两种方法:

public String concatBuilder() {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < howmany; ++i) {
        sb.append(i);
    }
    return sb.toString();
}

public String concatPlain() {
    String result = "";
    for (int i = 0; i < howmany; ++i) {
        result = result + i;
    }
    return result;
}

My java versions are the following: 我的java版本如下:

java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)

The JMH version is 1.20 JMH版本是1.20

Here is the output I get from javap -c LoopTest.class : 这是我从javap -c LoopTest.class获得的输出:

Method concatBuilder() that utilises StringBuilder explicitly looks exactly the same for java8 and java9: 使用StringBuilder方法concatBuilder()显式地看起来与java8和java9完全相同:

public java.lang.String concatBuilder();
Code:
   0: new           #17                 // class java/lang/StringBuilder
   3: dup
   4: invokespecial #18                 // Method java/lang/StringBuilder."<init>":()V
   7: astore_1
   8: iconst_0
   9: istore_2
  10: iload_2
  11: aload_0
  12: getfield      #19                 // Field howmany:I
  15: if_icmpge     30
  18: aload_1
  19: iload_2
  20: invokevirtual #20                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  23: pop
  24: iinc          2, 1
  27: goto          10
  30: aload_1
  31: invokevirtual #21                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  34: areturn

Note that the invocation of StringBuilder.append happens inside the loop, while StringBuilder.toString is called outside of it. 请注意, StringBuilder.append的调用发生在循环内部,而StringBuilder.toString在其外部调用。 This is important - it means that there will be no intermediate objects created. 这很重要 - 这意味着不会创建任何中间对象。 In java8 bytecode it's a bit different: 在java8字节码中它有点不同:

Method concatPlain() in Java8: concatPlain()方法concatPlain()

public java.lang.String concatPlain();
Code:
   0: ldc           #22                 // String
   2: astore_1
   3: iconst_0
   4: istore_2
   5: iload_2
   6: aload_0
   7: getfield      #19                 // Field howmany:I
  10: if_icmpge     38
  13: new           #17                 // class java/lang/StringBuilder
  16: dup
  17: invokespecial #18                 // Method java/lang/StringBuilder."<init>":()V
  20: aload_1
  21: invokevirtual #23                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  24: iload_2
  25: invokevirtual #20                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  28: invokevirtual #21                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  31: astore_1
  32: iinc          2, 1
  35: goto          5
  38: aload_1
  39: areturn

You can see that in java8 both StringBuilder.append and StringBuilder.toString are called inside the loop statement which means that it doesn't even try to omit creation of intermediate objects! 你可以看到在java8中,在循环语句中调用了StringBuilder.appendStringBuilder.toString ,这意味着它甚至都没有尝试省略中间对象的创建! It can be described in the code below: 它可以在下面的代码中描述:

public String concatPlain() {
    String result = "";
    for (int i = 0; i < howmany; ++i) {
        result = result + i;
        result = new StringBuilder().append(result).append(i).toString();
    }
    return result;
}

This explains performance difference between concatPlain() and concatBuilder() (which is few thousand times(!)). 这解释了concatPlain()concatBuilder()之间的性能差异concatPlain()几千次(!))。 The same issue happening with java9 - it doesn't try to avoid intermediate objects inside a loop, but it does a slightly better job inside a loop than java8 does (performance results are added): java9也会出现同样的问题 - 它不会试图避免循环中的中间对象,但它在循环内的工作比java8更好(添加了性能结果):

Method concatPlain() Java9: 方法concatPlain() Java9:

public java.lang.String concatPlain();
Code:
   0: ldc           #22                 // String
   2: astore_1
   3: iconst_0
   4: istore_2
   5: iload_2
   6: aload_0
   7: getfield      #19                 // Field howmany:I
  10: if_icmpge     27
  13: aload_1
  14: iload_2
  15: invokedynamic #23,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String;
  20: astore_1
  21: iinc          2, 1
  24: goto          5
  27: aload_1
  28: areturn

Here are performance results: 以下是效果结果:

JAVA 8: JAVA 8:

# Run complete. Total time: 00:02:18

Benchmark               (howmany)  Mode  Cnt     Score      Error  Units
LoopTest.concatBuilder     100000  avgt    5     2.098 ±    0.027  ms/op
LoopTest.concatPlain       100000  avgt    5  6908.737 ± 1227.681  ms/op

JAVA 9: JAVA 9:

For java 9 there are different strategies defined with -Djava.lang.invoke.stringConcat . 对于java 9,使用-Djava.lang.invoke.stringConcat定义了不同的策略。 I tried all of them: 我尝试了所有这些:

Default ( MH_INLINE_SIZED_EXACT ): 默认( MH_INLINE_SIZED_EXACT ):

# Run complete. Total time: 00:02:30
Benchmark               (howmany)  Mode  Cnt     Score    Error  Units
LoopTest.concatBuilder     100000  avgt    5     1.625 ±  0.015  ms/op
LoopTest.concatPlain       100000  avgt    5  4812.022 ± 73.453  ms/op

-Djava.lang.invoke.stringConcat= BC_SB -Djava.lang.invoke.stringConcat = BC_SB

# Run complete. Total time: 00:02:28
Benchmark               (howmany)  Mode  Cnt     Score    Error  Units
LoopTest.concatBuilder     100000  avgt    5     1.501 ±  0.024  ms/op
LoopTest.concatPlain       100000  avgt    5  4803.543 ± 53.825  ms/op

-Djava.lang.invoke.stringConcat= BC_SB_SIZED -Djava.lang.invoke.stringConcat = BC_SB_SIZED

# Run complete. Total time: 00:02:17
Benchmark               (howmany)  Mode  Cnt     Score     Error  Units
LoopTest.concatBuilder     100000  avgt    5     1.546 ±   0.027  ms/op
LoopTest.concatPlain       100000  avgt    5  4941.226 ± 422.704  ms/op

-Djava.lang.invoke.stringConcat= BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat = BC_SB_SIZED_EXACT

# Run complete. Total time: 00:02:45
Benchmark               (howmany)  Mode  Cnt      Score     Error  Units
LoopTest.concatBuilder     100000  avgt    5      1.560 ±   0.073  ms/op
LoopTest.concatPlain       100000  avgt    5  11390.665 ± 232.269  ms/op

-Djava.lang.invoke.stringConcat= BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat = BC_SB_SIZED_EXACT

# Run complete. Total time: 00:02:16
Benchmark               (howmany)  Mode  Cnt     Score     Error  Units
LoopTest.concatBuilder     100000  avgt    5     1.616 ±   0.030  ms/op
LoopTest.concatPlain       100000  avgt    5  8524.200 ± 219.499  ms/op

-Djava.lang.invoke.stringConcat= MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat = MH_SB_SIZED_EXACT

# Run complete. Total time: 00:02:17
Benchmark               (howmany)  Mode  Cnt     Score     Error  Units
LoopTest.concatBuilder     100000  avgt    5     1.633 ±   0.058  ms/op
LoopTest.concatPlain       100000  avgt    5  8499.228 ± 972.832  ms/op

-Djava.lang.invoke.stringConcat= MH_INLINE_SIZED_EXACT (yes, it's the default one but I decided to set it explicitly for clarity of experiment) -Djava.lang.invoke.stringConcat = MH_INLINE_SIZED_EXACT (是的,这是默认的但我决定明确地设置它以便实验清晰)

# Run complete. Total time: 00:02:23
Benchmark               (howmany)  Mode  Cnt     Score    Error  Units
LoopTest.concatBuilder     100000  avgt    5     1.654 ±  0.015  ms/op
LoopTest.concatPlain       100000  avgt    5  4812.231 ± 54.061  ms/op

I decided to investigate memory usage but didn't find anything interesting except that java9 consumes more memory. 我决定调查内存使用情况,但除了java9消耗更多内存之外没有发现任何有趣的内容。 Attached screenshots in case anybody would be interested. 附有截图以防任何人感兴趣。 Of course, they were made after the actual performance measurements, but not during them. 当然,它们是在实际性能测量之后制造的,但不是在它们期间制造的。

Java8 concatBuilder(): Java8 concatBuilder(): Java8 concatBuilder() Java8 concatPlain(): Java8 concatPlain(): 在此输入图像描述 Java9 concatBuilder(): Java9 concatBuilder(): 在此输入图像描述 Java9 concatPlain(): Java9 concatPlain(): 在此输入图像描述

So yeah, answering your question I can say that neither java8 nor java9 can avoid creating intermediate objects inside a loop. 所以是的,回答你的问题我可以说java8和java9都不能避免在循环中创建中间对象。

UPDATE: 更新:

As pointed out by @Eugene naked bytecode migt be meaningless since JIT does a lot of optimizations in runtime which looks logical to me, so I decided to add the output of optimized by JIT code (captured by -XX:CompileCommand=print,*LoopTest.concatPlain ). 正如@Eugene指出的裸字节码migt毫无意义,因为JIT在运行时进行了很多优化,这对我来说是合乎逻辑的,所以我决定添加由JIT代码优化的输出(由-XX:CompileCommand=print,*LoopTest.concatPlain捕获-XX:CompileCommand=print,*LoopTest.concatPlain )。

JAVA 8: JAVA 8:

0x00007f8c2d216d29: callq   0x7f8c2d0fdea0    ; OopMap{rsi=Oop [96]=Oop off=1550}
                                            ;*synchronization entry
                                            ; - org.sample.LoopTest::concatPlain@-1 (line 73)
                                            ;   {runtime_call}
0x00007f8c2d216d2e: jmpq    0x7f8c2d216786
0x00007f8c2d216d33: mov     %rdx,%rdx
0x00007f8c2d216d36: callq   0x7f8c2d0fa1a0    ; OopMap{r9=Oop [96]=Oop off=1563}
                                            ;*new  ; - org.sample.LoopTest::concatPlain@13 (line 75)
                                            ;   {runtime_call}
0x00007f8c2d216d3b: jmpq    0x7f8c2d2167e6
0x00007f8c2d216d40: mov     %rbx,0x8(%rsp)
0x00007f8c2d216d45: movq    $0xffffffffffffffff,(%rsp)
0x00007f8c2d216d4d: callq   0x7f8c2d0fdea0    ; OopMap{r9=Oop [96]=Oop rax=Oop off=1586}
                                            ;*synchronization entry
                                            ; - java.lang.StringBuilder::<init>@-1 (line 89)
                                            ; - org.sample.LoopTest::concatPlain@17 (line 75)
                                            ;   {runtime_call}
0x00007f8c2d216d52: jmpq    0x7f8c2d21682d
0x00007f8c2d216d57: mov     %rbx,0x8(%rsp)
0x00007f8c2d216d5c: movq    $0xffffffffffffffff,(%rsp)
0x00007f8c2d216d64: callq   0x7f8c2d0fdea0    ; OopMap{r9=Oop [96]=Oop rax=Oop off=1609}
                                            ;*synchronization entry
                                            ; - java.lang.AbstractStringBuilder::<init>@-1 (line 67)
                                            ; - java.lang.StringBuilder::<init>@3 (line 89)
                                            ; - org.sample.LoopTest::concatPlain@17 (line 75)
                                            ;   {runtime_call}
0x00007f8c2d216d69: jmpq    0x7f8c2d216874
0x00007f8c2d216d6e: mov     %rbx,0x8(%rsp)
0x00007f8c2d216d73: movq    $0xffffffffffffffff,(%rsp)
0x00007f8c2d216d7b: callq   0x7f8c2d0fdea0    ; OopMap{r9=Oop [96]=Oop rax=Oop off=1632}
                                            ;*synchronization entry
                                            ; - java.lang.Object::<init>@-1 (line 37)
                                            ; - java.lang.AbstractStringBuilder::<init>@1 (line 67)
                                            ; - java.lang.StringBuilder::<init>@3 (line 89)
                                            ; - org.sample.LoopTest::concatPlain@17 (line 75)
                                            ;   {runtime_call}
0x00007f8c2d216d80: jmpq    0x7f8c2d2168bb
0x00007f8c2d216d85: callq   0x7f8c2d0faa60    ; OopMap{r9=Oop [96]=Oop r13=Oop off=1642}
                                            ;*newarray
                                            ; - java.lang.AbstractStringBuilder::<init>@6 (line 68)
                                            ; - java.lang.StringBuilder::<init>@3 (line 89)
                                            ; - org.sample.LoopTest::concatPlain@17 (line 75)
                                            ;   {runtime_call}
0x00007f8c2d216d8a: jmpq    0x7f8c2d21693a
0x00007f8c2d216d8f: mov     %rdx,0x8(%rsp)
0x00007f8c2d216d94: movq    $0xffffffffffffffff,(%rsp)
0x00007f8c2d216d9c: callq   0x7f8c2d0fdea0    ; OopMap{r9=Oop [96]=Oop r13=Oop off=1665}
                                            ;*synchronization entry
                                            ; - java.lang.StringBuilder::append@-1 (line 136)
                                            ; - org.sample.LoopTest::concatPlain@21 (line 75)
                                            ;   {runtime_call}
0x00007f8c2d216da1: jmpq    0x7f8c2d216a1c
0x00007f8c2d216da6: mov     %rdx,0x8(%rsp)
0x00007f8c2d216dab: movq    $0xffffffffffffffff,(%rsp)
0x00007f8c2d216db3: callq   0x7f8c2d0fdea0    ; OopMap{[80]=Oop [96]=Oop off=1688}
                                            ;*synchronization entry
                                            ; - java.lang.StringBuilder::append@-1 (line 208)
                                            ; - org.sample.LoopTest::concatPlain@25 (line 75)
                                            ;   {runtime_call}
0x00007f8c2d216db8: jmpq    0x7f8c2d216b08
0x00007f8c2d216dbd: mov     %rdx,0x8(%rsp)
0x00007f8c2d216dc2: movq    $0xffffffffffffffff,(%rsp)
0x00007f8c2d216dca: callq   0x7f8c2d0fdea0    ; OopMap{[80]=Oop [96]=Oop off=1711}
                                            ;*synchronization entry
                                            ; - java.lang.StringBuilder::toString@-1 (line 407)
                                            ; - org.sample.LoopTest::concatPlain@28 (line 75)
                                            ;   {runtime_call}
0x00007f8c2d216dcf: jmpq    0x7f8c2d216bf8
0x00007f8c2d216dd4: mov     %rdx,%rdx
0x00007f8c2d216dd7: callq   0x7f8c2d0fa1a0    ; OopMap{[80]=Oop [96]=Oop off=1724}
                                            ;*new  ; - java.lang.StringBuilder::toString@0 (line 407)
                                            ; - org.sample.LoopTest::concatPlain@28 (line 75)
                                            ;   {runtime_call}
0x00007f8c2d216ddc: jmpq    0x7f8c2d216c39
0x00007f8c2d216de1: mov     %rax,0x8(%rsp)
0x00007f8c2d216de6: movq    $0x23,(%rsp)
0x00007f8c2d216dee: callq   0x7f8c2d0fdea0    ; OopMap{[96]=Oop [104]=Oop off=1747}
                                            ;*goto
                                            ; - org.sample.LoopTest::concatPlain@35 (line 74)
                                            ;   {runtime_call}
0x00007f8c2d216df3: jmpq    0x7f8c2d216cae

As you can see StringBuilder::toString is invoked before the goto which means that everything is happening inside the loop. 正如您所见,在goto之前调用了StringBuilder::toString ,这意味着循环中发生了一切。 Similar situation with java9 - StringConcatHelper::newString is invoked before the goto command. java9的类似情况 - 在goto命令之前调用StringConcatHelper::newString newString。

JAVA 9: JAVA 9:

0x00007fa1256548a4: mov     %ebx,%r13d
0x00007fa1256548a7: sub     0xc(%rsp),%r13d   ;*isub {reexecute=0 rethrow=0 return_oop=0}
                                            ; - java.lang.StringConcatHelper::prepend@5 (line 329)
                                            ; - java.lang.invoke.DirectMethodHandle$Holder::invokeStatic@16
                                            ; - java.lang.invoke.LambdaForm$BMH/127835623::reinvoke@172
                                            ; - java.lang.invoke.LambdaForm$MH/1587176117::linkToTargetMethod@6
                                            ; - org.sample.LoopTest::concatPlain@15 (line 75)

0x00007fa1256548ac: test    %r13d,%r13d
0x00007fa1256548af: jl      0x7fa125654b11
0x00007fa1256548b5: mov     %r13d,%r10d
0x00007fa1256548b8: add     %r9d,%r10d
0x00007fa1256548bb: mov     0x20(%rsp),%r11d
0x00007fa1256548c0: cmp     %r10d,%r11d
0x00007fa1256548c3: jb      0x7fa125654b11    ;*invokestatic arraycopy {reexecute=0 rethrow=0 return_oop=0}
                                            ; - java.lang.String::getBytes@22 (line 2993)
                                            ; - java.lang.StringConcatHelper::prepend@11 (line 330)
                                            ; - java.lang.invoke.DirectMethodHandle$Holder::invokeStatic@16
                                            ; - java.lang.invoke.LambdaForm$BMH/127835623::reinvoke@172
                                            ; - java.lang.invoke.LambdaForm$MH/1587176117::linkToTargetMethod@6
                                            ; - org.sample.LoopTest::concatPlain@15 (line 75)

0x00007fa1256548c9: test    %r9d,%r9d
0x00007fa1256548cc: jbe     0x7fa1256548ef
0x00007fa1256548ce: movsxd  %r9d,%rdx
0x00007fa1256548d1: lea     (%r12,%r8,8),%r10  ;*getfield value {reexecute=0 rethrow=0 return_oop=0}
                                            ; - java.lang.String::length@1 (line 669)
                                            ; - java.lang.StringConcatHelper::mixLen@2 (line 116)
                                            ; - java.lang.invoke.DirectMethodHandle$Holder::invokeStatic@11
                                            ; - java.lang.invoke.LambdaForm$BMH/127835623::reinvoke@105
                                            ; - java.lang.invoke.LambdaForm$MH/1587176117::linkToTargetMethod@6
                                            ; - org.sample.LoopTest::concatPlain@15 (line 75)

0x00007fa1256548d5: lea     0x10(%r12,%r8,8),%rdi
0x00007fa1256548da: mov     %rcx,%r10
0x00007fa1256548dd: lea     0x10(%rcx,%r13),%rsi
0x00007fa1256548e2: movabs  $0x7fa11db9d640,%r10
0x00007fa1256548ec: callq   %r10              ;*invokestatic arraycopy {reexecute=0 rethrow=0 return_oop=0}
                                            ; - java.lang.String::getBytes@22 (line 2993)
                                            ; - java.lang.StringConcatHelper::prepend@11 (line 330)
                                            ; - java.lang.invoke.DirectMethodHandle$Holder::invokeStatic@16
                                            ; - java.lang.invoke.LambdaForm$BMH/127835623::reinvoke@172
                                            ; - java.lang.invoke.LambdaForm$MH/1587176117::linkToTargetMethod@6
                                            ; - org.sample.LoopTest::concatPlain@15 (line 75)

0x00007fa1256548ef: cmp     0xc(%rsp),%ebx
0x00007fa1256548f3: jne     0x7fa125654cb9    ;*ifeq {reexecute=0 rethrow=0 return_oop=0}
                                            ; - java.lang.StringConcatHelper::newString@1 (line 343)
                                            ; - java.lang.invoke.DirectMethodHandle$Holder::invokeStatic@14
                                            ; - java.lang.invoke.LambdaForm$BMH/127835623::reinvoke@194
                                            ; - java.lang.invoke.LambdaForm$MH/1587176117::linkToTargetMethod@6
                                            ; - org.sample.LoopTest::concatPlain@15 (line 75)

0x00007fa1256548f9: mov     0x60(%r15),%rax
0x00007fa1256548fd: mov     %rax,%r10
0x00007fa125654900: add     $0x18,%r10
0x00007fa125654904: cmp     0x70(%r15),%r10
0x00007fa125654908: jnb     0x7fa125654aa5
0x00007fa12565490e: mov     %r10,0x60(%r15)
0x00007fa125654912: prefetchnta 0x100(%r10)
0x00007fa12565491a: mov     0x18(%rsp),%rsi
0x00007fa12565491f: mov     0xb0(%rsi),%r10
0x00007fa125654926: mov     %r10,(%rax)
0x00007fa125654929: movl    $0xf80002da,0x8(%rax)  ;   {metadata('java/lang/String')}
0x00007fa125654930: mov     %r12d,0xc(%rax)
0x00007fa125654934: mov     %r12,0x10(%rax)   ;*new {reexecute=0 rethrow=0 return_oop=0}
                                            ; - java.lang.StringConcatHelper::newString@36 (line 346)
                                            ; - java.lang.invoke.DirectMethodHandle$Holder::invokeStatic@14
                                            ; - java.lang.invoke.LambdaForm$BMH/127835623::reinvoke@194
                                            ; - java.lang.invoke.LambdaForm$MH/1587176117::linkToTargetMethod@6
                                            ; - org.sample.LoopTest::concatPlain@15 (line 75)

0x00007fa125654938: mov     0x30(%rsp),%r10
0x00007fa12565493d: shr     $0x3,%r10
0x00007fa125654941: mov     %r10d,0xc(%rax)   ;*synchronization entry
                                            ; - java.lang.StringConcatHelper::newString@-1 (line 343)
                                            ; - java.lang.invoke.DirectMethodHandle$Holder::invokeStatic@14
                                            ; - java.lang.invoke.LambdaForm$BMH/127835623::reinvoke@194
                                            ; - java.lang.invoke.LambdaForm$MH/1587176117::linkToTargetMethod@6
                                            ; - org.sample.LoopTest::concatPlain@15 (line 75)

0x00007fa125654945: mov     0x8(%rsp),%ebx
0x00007fa125654949: incl    %ebx              ; ImmutableOopMap{rax=Oop [0]=Oop }
                                            ;*goto {reexecute=1 rethrow=0 return_oop=0}
                                            ; - org.sample.LoopTest::concatPlain@24 (line 74)

0x00007fa12565494b: test    %eax,0x1a8996af(%rip)  ;*goto {reexecute=0 rethrow=0 return_oop=0}
                                            ; - org.sample.LoopTest::concatPlain@24 (line 74)
                                            ;   {poll}

Your loop is creating a new String each time. 你的循环每次都在创建一个新的String。 StringBuilder (not StringBuffer, which is synchronized and should not be used,) avoids instantiating a new object each time. StringBuilder(不是StringBuffer,已同步且不应使用),避免每次实例化一个新对象。

Java 9 may be adding new features, but I would be surprised if things have changed. Java 9可能正在添加新功能,但如果事情发生变化我会感到惊讶。 This issue is much older than Java 8. 这个问题比Java 8早得多。

Addition: 加成:

Java 9 has modified the way String concatenation is performed when using the "+" operator in a single statement. Java 9修改了在单个语句中使用“+”运算符时执行字符串连接的方式。 Up to Java 8, it used a builder. 在Java 8之前,它使用了一个构建器。 Now, it uses a more efficient approach. 现在,它使用更有效的方法。 However, that does not address using "+=" in a loop. 但是,这并不涉及在循环中使用“+ =”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM