Don’t ask me how many objects are created by the new string! Let me prove it to you!
I think all Java programmers have been troubled by this new string question. This is a high-frequency Java interview question, but unfortunately, there are different opinions on the Internet, and there is no standard answer. Some people say they created one object, others say they created two objects, and others say they may have created one or two objects, but no one has come up with evidence to kill each other, which puts us melon eaters in a dilemma. We don't know who to trust.
But today, Lao Wang dared to talk to everyone about this topic and show some evidence by the way.
According to the current situation, there are three answers to the number of objects created by new string ("XXX"):
The key controversy of multiple answers lies in the "string constant pool". Some say that the new string method will create a string object in the constant pool. Others say that the new string will not create an object in the string constant pool, but will detect and create a string in the string constant pool when calling the Intern () method.
Let's talk about the "string constant pool" first.
String constant pool
String allocation, like other object allocation, takes a high cost of time and space. If you need to create a large number of strings frequently, it will greatly affect the performance of the program. Therefore, in order to improve performance and reduce memory overhead, the JVM introduces the concept of string constant pool table.
String constant pool is equivalent to opening up a constant pool space for strings, which is similar to cache, For a directly assigned string (string s = "XXX"), the string that already exists in the string constant pool is preferentially used every time a string is created. If there is no relevant string in the string constant pool, the string will be created in the string constant pool first, and then the reference address will be returned to the variable, as shown in the following figure:
public class StringExample {
public static void main(String[] args) {
String s1 = "Java";
String s2 = "Java";
System.out.println(s1 == s2);
}
}
The execution result of the above program is: true, indicating that variable S1 and variable S2 point to the same address.
By the way, let's talk about the changes of string constant pool in different JDK versions.
Memory layout of constant pool
After JDK 1.7, the immortality is replaced by a meta space, and the string constant pool is moved from the method area to the Java heap.
The memory layout of JDK 1.7 is shown in the following figure:
Answer decryption
People who think that new creates an object think that new string just creates an object on the heap. Only when using intern() can they find and create a string in the constant pool.
People who think that two objects are created in the new method think that new string will create an object on the heap and a string in the string constant pool.
People who think it is possible to create one or two objects in the new method think that the new string will first go to the constant pool to judge whether there is this string. If there is, only one string will be created on the heap and point to the string in the constant pool. If there is no such string in the constant pool, two objects will be created and this string will be created in the constant pool first, Then return this reference to the object on the heap, as shown in the following figure:
Lao Wang thinks the correct answer is to create one or two objects.
technological verification
The person who tied the bell must answer the bell. Back to the controversial point of the question, will new string create characters in the constant pool? We can draw a correct conclusion by decompiling the following code. The code is as follows:
public class StringExample {
public static void main(String[] args) {
String s1 = new String("javaer-wang");
String s2 = "wang-javaer";
String s3 = "wang-javaer";
}
}
First, we use javac stringexample Compile the code in Java, and then use javap - V stringexample to view the compilation results. The relevant information is as follows:
Classfile /Users/admin/github/blog-example/blog-example/src/main/java/com/example/StringExample.class
Last modified 2020年4月16日; size 401 bytes
SHA-256 checksum 89833a7365ef2930ac1bc3d7b88dcc5162da4b98996eaac397940d8997c94d8e
Compiled from "StringExample.java"
public class com.example.StringExample
minor version: 0
major version: 58
flags: (0x0021) ACC_PUBLIC,ACC_SUPER
this_class: #16 // com/example/StringExample
super_class: #2 // java/lang/Object
interfaces: 0,fields: 0,methods: 2,attributes: 1
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Class #8 // java/lang/String
#8 = Utf8 java/lang/String
#9 = String #10 // javaer-wang
#10 = Utf8 javaer-wang
#11 = Methodref #7.#12 // java/lang/String."<init>":(Ljava/lang/String;)V
#12 = NameAndType #5:#13 // "<init>":(Ljava/lang/String;)V
#13 = Utf8 (Ljava/lang/String;)V
#14 = String #15 // wang-javaer
#15 = Utf8 wang-javaer
#16 = Class #17 // com/example/StringExample
#17 = Utf8 com/example/StringExample
#18 = Utf8 Code
#19 = Utf8 LineNumberTable
#20 = Utf8 main
#21 = Utf8 ([Ljava/lang/String;)V
#22 = Utf8 SourceFile
#23 = Utf8 StringExample.java
{
public com.example.StringExample();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1,locals=1,args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC,ACC_STATIC
Code:
stack=3,locals=4,args_size=1
0: new #7 // class java/lang/String
3: dup
4: ldc #9 // String javaer-wang
6: invokespecial #11 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: ldc #14 // String wang-javaer
12: astore_2
13: ldc #14 // String wang-javaer
15: astore_3
16: return
LineNumberTable:
line 5: 0
line 6: 10
line 7: 13
line 8: 16
}
SourceFile: "StringExample.java"
Where constant pool represents the string constant pool. We found string S1 = new string ("javaer Wang") in the string constant pool during string compilation; The defined "javaer Wang" character can be seen in the message #10 = utf8 javaer Wang, that is, the string created in the new method will be put into the string constant pool in the compilation period, that is, the new string method will first judge the string constant pool. If there is no string, two objects will be created, If it already exists, only one object will be created in the heap to point to the string in the string constant pool.
So the question is, is the execution result of the following code true or false?
String s1 = new String("javaer-wang");
String s2 = new String("javaer-wang");
System.out.println(s1 == s2);
Since new string will create a string in the constant pool, the execution result should be true. In fact, it is not. The addresses on the heap of variables S1 and S2 are compared here. Because the addresses on the heap are different, the result must be false, as shown in the following figure:
public static void main(String[] args) {
String s1 = "Java";
String s2 = "Java";
String s3 = new String("Java");
String s4 = new String("Java");
System.out.println(s1 == s2);
System.out.println(s3 == s4);
}
The results of program implementation also meet expectations:
Expand knowledge
We know that string is modified by final, that is, it must be assigned and cannot be modified. However, in addition to optimizing the string constant pool, the compiler also optimizes the strings that can be confirmed at compile time, such as the following code:
public static void main(String[] args) {
String s1 = "abc";
String s2 = "ab" + "c";
String s3 = "a" + "b" + "c";
System.out.println(s1 == s2);
System.out.println(s1 == s3);
}
According to the idea that string cannot be modified, S2 should create two strings "ab" and "C" in the string constant pool, and S3 will create three strings. Their reference comparison result must be false, but in fact, it is not. Their results are true, which is the credit of compiler optimization.
Similarly, we use javac stringexample Java compile the code first, and then use the javap - C stringexample command to view the compiled code as follows:
警告: 文件 ./StringExample.class 不包含类 StringExample
Compiled from "StringExample.java"
public class com.example.StringExample {
public com.example.StringExample();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #7 // String abc
2: astore_1
3: ldc #7 // String abc
5: astore_2
6: ldc #7 // String abc
8: astore_3
9: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
12: aload_1
13: aload_2
14: if_acmpne 21
17: iconst_1
18: goto 22
21: iconst_0
22: invokevirtual #15 // Method java/io/PrintStream.println:(Z)V
25: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_1
29: aload_3
30: if_acmpne 37
33: iconst_1
34: goto 38
37: iconst_0
38: invokevirtual #15 // Method java/io/PrintStream.println:(Z)V
41: return
}
It can be seen from codes 3 and 6 that the strings have been optimized by the compiler into the string "ABC".