We have covered constant pools in the JVM memory Model section. In this article, we will cover String String constant pools in more detail.

For the most part the following content from zhihu R friend answer: www.zhihu.com/question/55… Recommend another article: javaranch.com/journal/200…

When do string literals enter the string constant pool?

Code first. What’s more straightforward than code

//JDK7+ 
public class StringTest {
    private  static String s1 = "static";
    public static void main(String[] args) {
        String hello1 = new String("hell") + new String("o");
        String hello2 = new String("he") + new String("llo");
        String hello3 = hello1.intern();
        String hello4 = hello2.intern();
        System.out.println(hello1 == hello3); // true
        System.out.println(hello1 == hello4); // true}}Copy the code

Constant pool = Constant pool = Constant pool = Constant pool = Constant pool = Constant pool

Classfile /E:/workspace/VariousCases/target/classes/cn/onenine/jvm/constantpool/StringTest.class
  Last modified 2021-8-3; size 1299 bytes
  MD5 checksum 338bd0034155ec3bf8d608540a31761c
  Compiled from "StringTest.java"
public class cn.onenine.jvm.constantpool.StringTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // cn/onenine/jvm/constantpool/StringTest
   #2 = Utf8               cn/onenine/jvm/constantpool/StringTest
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               s1
   #6 = Utf8               Ljava/lang/String;
   #7 = Utf8               <clinit>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = String             #11            // static   
  #11 = Utf8               static
  #12 = Fieldref           #1.#13         // cn/onenine/jvm/constantpool/StringTest.s1:Ljava/lang/String;
  #13 = NameAndType        #5:#6          // s1:Ljava/lang/String;
  #14 = Utf8               LineNumberTable
  #15 = Utf8               LocalVariableTable
  #16 = Utf8               <init>
  #17 = Methodref          #3.#18         // java/lang/Object."<init>":()V
  #18 = NameAndType        #16:#8         // "<init>":()V
  #19 = Utf8               this
  #20 = Utf8               Lcn/onenine/jvm/constantpool/StringTest;
  #21 = Utf8               main
  #22 = Utf8               ([Ljava/lang/String;)V
  #23 = Class              #24            // java/lang/StringBuilder
  #24 = Utf8               java/lang/StringBuilder
  #25 = Class              #26            // java/lang/String
  #26 = Utf8               java/lang/String
  #27 = String             #28            // hell
  #28 = Utf8               hell
  #29 = Methodref          #25.#30        // java/lang/String."<init>":(Ljava/lang/String;)V
  #30 = NameAndType        #16:#31        // "<init>":(Ljava/lang/String;)V
  #31 = Utf8               (Ljava/lang/String;)V
  #32 = Methodref          #25.#33        // java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  #33 = NameAndType        #34:#35        // valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  #34 = Utf8               valueOf
  #35 = Utf8               (Ljava/lang/Object;)Ljava/lang/String;
  #36 = Methodref          #23.#30        // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
  #37 = String             #38            // o
  #38 = Utf8               o
  #39 = Methodref          #23.#40        // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #40 = NameAndType        #41:#42        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #41 = Utf8               append
  #42 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #43 = Methodref          #23.#44        // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #44 = NameAndType        #45:#46        // toString:()Ljava/lang/String;
  #45 = Utf8               toString
  #46 = Utf8               ()Ljava/lang/String;
  #47 = String             #48            // he
  #48 = Utf8               he
  #49 = String             #50            // llo
  #50 = Utf8               llo
  #51 = Methodref          #25.#52        // java/lang/String.intern:()Ljava/lang/String;
  #52 = NameAndType        #53:#46        // intern:()Ljava/lang/String;
  #53 = Utf8               intern
  #54 = Fieldref           #55.#57        // java/lang/System.out:Ljava/io/PrintStream;
  #55 = Class              #56            // java/lang/System
  #56 = Utf8               java/lang/System
  #57 = NameAndType        #58:#59        // out:Ljava/io/PrintStream;
  #58 = Utf8               out
  #59 = Utf8               Ljava/io/PrintStream;
  #60 = Methodref          #61.#63        // java/io/PrintStream.println:(Z)V
  #61 = Class              #62            // java/io/PrintStream
  #62 = Utf8               java/io/PrintStream
  #63 = NameAndType        #64:#65        // println:(Z)V
  #64 = Utf8               println
  #65 = Utf8               (Z)V
  #66 = Utf8               args
  #67 = Utf8               [Ljava/lang/String;
  #68 = Utf8               hello1
  #69 = Utf8               hello2
  #70 = Utf8               hello3
  #71 = Utf8               hello4
  #72 = Utf8               StackMapTable
  #73 = Class              #67            // "[Ljava/lang/String;"
  #74 = Utf8               SourceFile
  #75 = Utf8               StringTest.java
{
  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: ldc           #10                 // String static
         2: putstatic     #12                 // Field s1:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

  public cn.onenine.jvm.constantpool.StringTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #17                 // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcn/onenine/jvm/constantpool/StringTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=5, locals=5, args_size=1
         0: new           #23                 // class java/lang/StringBuilder
         3: dup
         4: new           #25                 // class java/lang/String
         7: dup
         8: ldc           #27                 // String hell
        10: invokespecial #29                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
        13: invokestatic  #32                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        16: invokespecial #36                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        19: new           #25                 // class java/lang/String
        22: dup
        23: ldc           #37                 // String o
        25: invokespecial #29                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
        28: invokevirtual #39                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        31: invokevirtual #43                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        34: astore_1
        35: new           #23                 // class java/lang/StringBuilder
        38: dup
        39: new           #25                 // class java/lang/String
        42: dup
        43: ldc           #47                 // String he
        45: invokespecial #29                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
        48: invokestatic  #32                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        51: invokespecial #36                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        54: new           #25                 // class java/lang/String
        57: dup
        58: ldc           #49                 // String llo
        60: invokespecial #29                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
        63: invokevirtual #39                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        66: invokevirtual #43                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        69: astore_2
        70: aload_1
        71: invokevirtual #51                 // Method java/lang/String.intern:()Ljava/lang/String;
        74: astore_3
        75: aload_2
        76: invokevirtual #51                 // Method java/lang/String.intern:()Ljava/lang/String;
        79: astore        4
        81: getstatic     #54                 // Field java/lang/System.out:Ljava/io/PrintStream;
        84: aload_1
        85: aload_3
        86: if_acmpne     93
        89: iconst_1
        90: goto          94
        93: iconst_0
        94: invokevirtual #60                 // Method java/io/PrintStream.println:(Z)V
        97: getstatic     #54                 // Field java/lang/System.out:Ljava/io/PrintStream;
       100: aload_1
       101: aload         4
       103: if_acmpne     110
       106: iconst_1
       107: goto          111
       110: iconst_0
       111: invokevirtual #60                 // Method java/io/PrintStream.println:(Z)V
       114: return
      LineNumberTable:
        line 13: 0
        line 14: 35
        line 15: 70
        line 16: 75
        line 17: 81
        line 18: 97
        line 20: 114
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0     115     0  args   [Ljava/lang/String;
           35      80     1 hello1   Ljava/lang/String;
           70      45     2 hello2   Ljava/lang/String;
           75      40     3 hello3   Ljava/lang/String;
           81      34     4 hello4   Ljava/lang/String;
      StackMapTable: number_of_entries = 4
        frame_type = 255 /* full_frame */
          offset_delta = 93
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
        frame_type = 79 /* same_locals_1_stack_item */
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "StringTest.java"

Copy the code

During Class loading, the JVM does not immediately create instances of these string objects in the Class file constant pool in the heap and place references from the heap into the string constant pool. Instead, the JVM executes the Resolve phase of the Class, which the JVM specification explicitly specifies can be Lazy. In the JVM specification, there are two types of constant pool for Class files:

  1. CONSTANT_Utf8
  2. CONSTANT_String

The latter is the type of a String constant, but it does not hold the contents of a String constant directly. Instead, it holds only an index. The other constant pool specified by this index must be a constant of type CONSTANT_Utf8 in order to truly hold the contents of the String. #10 corresponds to the “static” string constant reference to **#11 that the static constant points to. In HotSpot VM, the runtime constant pool

  • CONSTANT_Utf8 -> Symbol* (a pointer to a C++ object of type Symbol, containing utf-8 encoded strings in the same format as the Class file)
  • CONSTANT_String -> java.lang.String (a reference to an actual Java object whose C++ type is oop)

CONSTANT_Utf8 is created entirely during class loading, while CONSTANT_String is lazy resolve, such as resolve when the LDC directive that first references this item is first executed. Before resolve, HotSpot VM calls its type JVM_CONSTANT_UnresolvedString. You can also see the LDC command in line 112:8: LDC #27 // String hell

So conclusion ** : in HotSpot, when loading a class, String literals go into the current class’s run-time constant pool, not into the global String constant pool (i.e. there is no reference to the String object in the String table, and no corresponding object generated in the heap).

What is the LDC command?

Push int,float, or String constants from the constant pool to the top of the stack

In the decompiled code above, line 112 8: LDC #27 // String hell means to push “hell” to the top of the stack. The string constant is lazy resolve. Is there an opportunity to create an object, to put a reference to an object into the string constant pool? **** is the LDC directive, which is the condition that triggers the lazy resolve action. The execution semantics of LDC bytecode here are: Find the index item in the Runtime constant pool of the current class, resolve the item if it has not already been resolved, and return the resolve value. When a String constant is encountered, the resolve procedure returns a java.lang.String reference if it finds that the String table already has a java.lang.String reference, otherwise, If not, a String object is created in the Java heap, the reference is recorded in the String table, and the reference is returned. That is, whether the LDC directive creates a new String instance depends on whether the String table has already recorded a reference to the same thing the first time the LDC directive is executed.

Runtime parsing

So first of all, what does String#intern do

  • In JDK6, the intern method copies the first encountered string instance into the persistent generation’s string constant pool and returns the string reference.
  • JDK7 and beyond, intern method doesn’t need to copy the instance of the string to the permanent generation, will first determine whether the constant pool already has the string, if have the direct return to its reference in the constant pool, or to save its reference to a string constant in the pool, and then returning to the reference, is to return reference, The String constant pool, also known as String Table, is covered in the JVM memory model.

Now that we have learned about String#intern, we can start analyzing the above code. For convenience, we can copy the above code:

//JDK7+ 
public class StringTest {
    private  static String s1 = "static"; (1)public static void main(String[] args) {
        String hello1 = new String("hell") + new String("o"); (2) String hello2 =new String("he") + new String("llo"); (3) String hello3 = hello1.intern(); (4) String hello4 = hello2.intern(); (5) System. The out. Println (hello1 = = hello3);// true (6)
        System.out.println(hello1 == hello4); // true (7)}}Copy the code

(1) static variable s1 refers to the “static” String object reference. The LDC directive pushes the “static” variable to the top of the stack (create an instance of the String, place the instance reference in the String table), and then assigns it to the putStatic directive.



(2) : Looking at the decompiled code, you can see that “hello” and “O” are pushed to the top of the stack by the LDC instruction. You can also see that for the “+” sign, the compiler creates a StringBuilder object and concatenates the string using the Append method, and assigns the concatenated string to the second local variable using the astore_1 instruction.Important: There is no LDC command to push the concatenated String “hello” to the top of the stack, i.e. no reference to “hello” is put into the String table.



(3) : Hello1 calls the intern method, and the hello1 variable holds a reference to the “Hello” instance in the heap. Since (2) “Hello” is not put into the global String constant pool, (3) the inern method will put a reference to the “Hello” instance object into the String table, Then return the reference to hello3, so that hello3 and Hello1 hold the same reference (both the address of the “hello” instance in the heap).

(4) : Hello2 calls the intern method, and the reference that hello2 holds is a reference to the instance of hello in the heap (different from the reference that Hello1 holds, though both instances of Hello). When hello2 calls intern, it finds a reference to “Hello” in the String table (hello1 holds an instance reference to “Hello”), returns the reference from the String table and assigns it to Hello4.

** Thus hello4, hello3, and hello1 hold the same reference, and hello2 holds a different reference from all of them. ** is represented by a graph as follows:

The following diagram shows the relationship between the local variable table, heap, and string constant pool