Preface
Among Java developers, the high resource occupancy of strings is often a hot topic.
Let's discuss in depth why it occupies high resources.
In Java, a string object is immutable, meaning that once it is created, you can't change it anymore. So when we splice strings, we create a new string, and the old one is marked by the garbage collector.
If we process millions of strings, then we generate millions of additional strings to be processed by the garbage collector.
In most tutorials, you may see that using + signs to splice strings will generate multiple Strings, resulting in poor performance. It is recommended to use StringBuffer/StringBuilder to splice.
But is this really the case?
This article has done the following experiment in JDK8:
public static void main(String[] args) { String result = ""; result += "some more data"; System.out.println(result); }Decompiling through javap -c to get:
Code: 0: aload_0 // Push 'this' on to the stack 1: invokespecial #1 // Invoke Object class constructor // pop 'this' ref from the stack 4: return // Return from constructor public static void main(java.lang.String[]); Code: 0: ldc #2 // Load constant #2 on to the stack 2: store_1 // Create local var from stack (pop #2) 3: new #3 // Push new StringBuilder ref on stack 6: dup // Duplicate value on top of the stack 7: invokespecial #4 // Invoke StringBuilder constructor // pop object reference 10: aload_1 // Push local variable containing #2 11: invokevirtual #5 // Invoke method StringBuilder.append() // pop obj reference + parameter // push result (StringBuilder ref) 14: ldc #6 // Push "some more data" on the stack 16: invokevirtual #5 // Invoke StringBuilder.append // pop twice, push result 19: invokevirtual #7 // Invoke StringBuilder.toString:(); 22: store_1 // Create local var from stack (pop #6) 23: getstatic #8 // Push value System.out:PrintStream 26: aload_1 // Push local variable containing #6 27: invokevirtual #9 // Invoke method PrintStream.println() // pop twice (object ref + parameter) 30: return // Return void from method
You can see that the Java compiler optimizes the generated bytecode, automatically creates a StringBuilder, and performs append operations.
Since the substrings of the final string are already known at compile time, the Java compiler will perform the above optimization in this case. This optimization is called a static string concatenation optimization and has been enabled since JDK5.
Does that mean that after JDK5, we no longer need to manually generate StringBuilder, and we can achieve the same performance through the + sign?
Let's try dynamically splicing strings:
Dynamic splicing strings refer to substrings that are known only at runtime. For example, adding a string to a loop:
public static void main(String[] args) { String result = ""; for (int i = 0; i < 10; i++) { result += "some more data"; } System.out.println(result); }Also decompiled:
Code: 0: aload_0 // Push 'this' on to the stack 1: invokespecial #1 // Invoke Object class constructor // pop 'this' ref from the stack 4: return // Return from constructor public static void main(java.lang.String[]); Code: 0: ldc #2 // Load constant #2 on to the stack 2: store_1 // Create local var from stack, pop #2 3: iconst_0 // Push value 0 onto the stack 4: istore_2 // Pop value and store it in local var 5: iload_2 // Push local var 2 on to the stack 6: i2d // Convert int to double on // top of stack (pop + push) 7: ldc2_w #3 // Push constant 10e6 on to the stack 10: dcmpg // Compare two doubles on top of stack // pop twice, push result: -1, 0 or 1 11: ifge 40 // if value on top of stack is greater // than or equal to 0 (pop once) // branch to instruction at code 40 14: new #5 // Push new StringBuilder ref on stack 17: dup // Duplicate value on top of the stack 18: invokespecial #6 // Invoke StringBuilder constructor // pop object reference 21: aload_1 // Push local var 1 (empty String) // on to the stack 22: invokevirtual #7 // Invoke StringBuilder.append // pop obj ref + param, push result 25: ldc #8 // Push "some more data" on the stack 27: invokevirtual #7 // Invoke StringBuilder.append // pop obj ref + param, push result 30: invokevirtual #9 // Invoke StringBuilder.toString // pop object reference 33: store_1 // Create local var from stack (pop) 34: iinc 2, 1 // Increment local variable 2 by 1 37: goto 5 // Move to instruction at code 5 40: getstatic #10 // Push value System.out:PrintStream 43: aload_1 // Push local var 1 (result String) 44: invokevirtual #11 // Invoke method PrintStream.println() // pop twice (object ref + parameter) 47: return // Return void from method
You can see that the StringBuilder was new at 14, but at 37, goto 5. During the loop, the optimization was not achieved, and new StringBuilders were constantly being generated.
So the above code is similar:
String result = "";for (int i = 0; i < 10; i++) { StringBuilder tmp = new StringBuilder(); tmp.append(result); tmp.append("some more data"); result = tmp.toString();}System.out.println(result);You can see that new StringBuilders are constantly generated, and through tostring, the original StringBuilder will no longer be referenced, as garbage, and also increases the GC cost.
Therefore, in actual use, when you cannot distinguish whether a string is static or dynamic, use StringBuilder.
Reference:
http://www.pellegrino.link/2015/08/22/string-concatenation-with-java-8.html
Summarize
The above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support to Wulin.com.