I think all Java programmers have been bothered by this new String question. This is a common Java interview question, but unfortunately there are so many opinions on the Internet that there is no standard answer. Some say one object was created, some say two, and some say maybe one or two, but neither has produced any evidence of killing the other, leaving us melon eaters in a dilemma as to whom to trust.

But today, Lao Wang dares to talk to you about this topic, and by the way take out some evidence.

As it stands, there are three answers to the number of objects created by the new String(” XXX “) :

  1. Somebody said I created an object;
  2. Someone said I created 2 objects;
  3. Someone said they created one or two objects.

In more than one answer key controversial points in the “pool” string constants, in some way that say new string constant pool create a string object, some people say that the new string string constant pool will not go when creating objects, but in the calling intern () method, to string constant pool detection and create a string.

Let’s start with the string constant pool.

String constant pool

String allocation, like other object allocation, takes a high cost of time and space. If a large number of strings are frequently created, the performance of the program will be greatly affected. Therefore, the JVM introduced the concept of Constant Pool Table of strings to improve performance and reduce memory overhead.

String constant pool is equivalent to a constant pool up space is similar to the buffer zone, direct assignment for strings (String s = “XXX”), in each time you create a String using existing String String constant pool, if there is no such String String constant pool, will create the String in the String constant pool, Then return the variable with the reference address, as shown below:


The above statement can be proved by the following code:

public class StringExample {
    public static void main(String[] args) {
        String s1 = "Java";
        String s2 = "Java"; System.out.println(s1 == s2); }}Copy the code

The result of the above program is true, indicating that s1 and s2 refer to the same address.

We’ll mention the string constant pool changes in different JDK versions.

Memory layout for constant pools

Since JDK 1.7, we have replaced immortal generations with meta-spaces, moving the string constant pool from the method area to the Java heap.

The JDK 1.7 memory layout is shown below:

The JDK 1.8 memory layout is shown below:

The biggest difference between JDK 1.8 and JDK 1.7 is that JDK 1.8 cancels the permanent generation and sets up a meta-space. Official to memory that is due to a permanent generation often enough or memory leaks occur, will be Java. Lang. OutOfMemoryError: PermGen anomalies, so the area will be permanently abandoned to yuan space, change in order to use the local memory space, website to explain details: openjdk.java.net/jeps/122

The answer to decrypt

People who think that new creates 1 object think that new String just creates an object on the heap and only looks up and creates strings in the constant pool when you use intern().

People who think that new creates two objects think that new String creates an object on the heap and also creates a String in the String constant pool.

People who think it’s possible to create one or two objects with new say that the new String will first check the constant pool for the String, and if it does, it will just create one String on the heap and point to the String in the constant pool, and if it doesn’t, it will create two objects, Create a new string in the constant pool, then return the reference to the object on the heap, as shown below:

Wang thinks the right answer is to create one or two objects.

Technology demonstration

Back to the controversial point of the question, does a new String create a character in a constant pool? We can get to the correct conclusion by decompiling the following code:

public class StringExample {
    public static void main(String[] args) {
        String s1 = new String("javaer-wang");
        String s2 = "wang-javaer";
        String s3 = "wang-javaer"; }}Copy the code

Javap -v StringExample javap -v StringExample javac StringExample

Classfile /Users/admin/github/blog-example/blog-example/src/main/java/com/example/StringExample.class Last modified 16 April 2020; 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."
      
       ":()V
      
   #2 = Class #4 // java/lang/Object
   #3 = NameAndType #5:#6 // "
      
       ":()V
      
   #4 = Utf8 java/lang/Object
   #5 = Utf8 
      
   #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."
      
       ":(Ljava/lang/String;) V
      
  #12 = NameAndType #5:#13 // "
      
       ":(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."
      
       ":()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."
      
       ":(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"` </pre>
Copy the code

Note: the above code also runs in jdk1.8.0_101.

S1 = new String(“javaer-wang”); s1 = new String(“javaer-wang”); The “javaer-wang” character is defined as #10 = Utf8 javaer-wang, which means that strings created at compile-time in the new mode are put into the compile-time string constant pool. So the new String method will evaluate the String constant pool first, if it doesn’t have a new String it will create two objects, if it already exists it will only create one object in the heap that points to the String in the String constant pool.

So the question is, is this code true or false?

`String s1 = new String("javaer-wang"); String s2 = new String("javaer-wang"); System.out.println(s1 == s2); `

Since new String creates a String in the constant pool, the result of the execution should be true. The heap address of s1 and s2 is different, so the result must be false, as shown in the following figure:

As can be seen from the figure, s1 and S2 references must be the same, while S3 and S4 references are different, and the corresponding program code is as follows:

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);
}
Copy the code

The results of program execution are also in line with expectations:

true

false

Expand the knowledge

We know that strings are final, which means that they cannot be modified without being assigned. But in addition to optimizations for string constant pools, the compiler also optimizes strings that can be validated 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);
}
Copy the code

S2 would create two strings “ab” and “c” in the String constant pool, s3 would create three strings, and their reference comparisons would be false, but they are not, they would both be true, thanks to compiler optimization.

Javap -c StringExample javap -c StringExample javac StringExample

Warning: The file./ stringexample. class does not contain the 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."
      
       ":()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
}
Copy the code

From Code 3 and 6 you can see that the strings are optimized by the compiler to the string “ABC”.

conclusion

In this article we looked at the compiled code in Javap -v XXX and found that new String creates this String for the first time in the String constant pool. That is, the way new creates a String may create one or two objects. If the string already exists in the constant pool, only one variable is created on the heap and points to the value in the string constant pool. If there are no related characters in the string constant pool, the string is created first before returning a variable referencing the string to the heap space. We also covered the string constant pool changes in JDK 1.7 and JDK 1.8 and compiler optimizations for determining strings to help you understand string comparisons.

Original Chinese community of pharaoh Java mp.weixin.qq.com/s/f6VTVXktA…