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".

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>