Talk about intern of java String

  jvm

Order

This article mainly studies the intern of java String.

String.intern()

java.base/java/lang/String.java

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {

    //......

    /**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     * It follows that for any two strings {@code s} and {@code t},
     * {@code s.intern() == t.intern()} is {@code true}
     * if and only if {@code s.equals(t)} is {@code true}.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section 3.10.5 of the
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     * @jls 3.10.5 String Literals
     */
    public native String intern();

    //......           

}               
  • When the intern method is called, if the constant pool already contains a String of equals this string object, the string in the pool is returned
  • When the intern method is called, if the constant pool does not have a String equals to this String object, add this String object to the pool and return a reference to this String object (That is, the intern method returns a reference to this String object in heap)
  • All literal strings and string-valued constant expressions are interned.

Example

Based on jdk12

StringExistInPoolBeforeIntern

public class StringExistInPoolBeforeIntern {

    public static void main(String[] args){
        String stringObject = new String("tomcat");
        //NOTE 在intern之前,string table已经有了tomcat,因而intern返回tomcat,不会指向stringObject
        stringObject.intern();
        String stringLiteral = "tomcat";
        System.out.println(stringObject == stringLiteral); //false
    }
}
  • Tomcat This literal string has been inter-passed. The constant pool does not have tomcat, so it is added to the constant pool. The constant pool has Tomcat; In addition, because stringObject is new, there is also a tomcat in heap, and at this time it points to tomcat in heap
  • Intern () returns tomcat; of constant pool in heap; StringLiteral is the literal string of tomcat. Since the constant pool already has this value, stringLiteral refers to tomcat of the constant pool in heap
  • At this time, stringObject refers to tomcat in heap, while stringLiteral is tomcat of constant pool in heap, so they are not equal and return false

StringNotExistInPoolBeforeIntern

public class StringNotExistInPoolBeforeIntern {

    public static void main(String[] args){
        String stringObject = new String("tom") + new String("cat");
        //NOTE 在intern之前,string table没有tomcat,因而intern指向stringObject
        stringObject.intern();
        String stringLiteral = "tomcat";
        System.out.println(stringObject == stringLiteral); //true
    }
}
  • Tom and cat are two literalstrings that have been inter-passed. The constant pool does not have tom and cat, so they are added to the constant pool. The constant pool has tom and Cat; In addition, because stringObject comes from new and is a concat of tom and cat, there is a tomcat in heap.
  • When stringObject’s intern method was executed, it was added to the constant pool because there was no tomcat in the constant pool. intern () returned a reference to tomcat in heap. StringLiteral is the literal string of tomcat. Since StringObject. int () has added tomcat to the constant pool and pointed to the reference to tomcat in heap, stringLiteral returns the reference to tomcat in heap
  • Since stringLiteral returns a reference to tomcat in heap, which is actually stringObject, the two are equal and return true

javap

Based on jdk12

StringExistInPoolBeforeIntern

javac src/main/java/com/example/javac/StringExistInPoolBeforeIntern.java
javap -v src/main/java/com/example/javac/StringExistInPoolBeforeIntern.class

  Last modified 2019年4月6日; size 683 bytes
  MD5 checksum 207635ffd7560f1df24b98607e2ca7db
  Compiled from "StringExistInPoolBeforeIntern.java"
public class com.example.javac.StringExistInPoolBeforeIntern
  minor version: 0
  major version: 56
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #8                          // com/example/javac/StringExistInPoolBeforeIntern
  super_class: #9                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
   #1 = Methodref          #9.#21         // java/lang/Object."<init>":()V
   #2 = Class              #22            // java/lang/String
   #3 = String             #23            // tomcat
   #4 = Methodref          #2.#24         // java/lang/String."<init>":(Ljava/lang/String;)V
   #5 = Methodref          #2.#25         // java/lang/String.intern:()Ljava/lang/String;
   #6 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = Methodref          #18.#28        // java/io/PrintStream.println:(Z)V
   #8 = Class              #29            // com/example/javac/StringExistInPoolBeforeIntern
   #9 = Class              #30            // java/lang/Object
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               StackMapTable
  #17 = Class              #31            // "[Ljava/lang/String;"
  #18 = Class              #32            // java/io/PrintStream
  #19 = Utf8               SourceFile
  #20 = Utf8               StringExistInPoolBeforeIntern.java
  #21 = NameAndType        #10:#11        // "<init>":()V
  #22 = Utf8               java/lang/String
  #23 = Utf8               tomcat
  #24 = NameAndType        #10:#33        // "<init>":(Ljava/lang/String;)V
  #25 = NameAndType        #34:#35        // intern:()Ljava/lang/String;
  #26 = Class              #36            // java/lang/System
  #27 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;
  #28 = NameAndType        #39:#40        // println:(Z)V
  #29 = Utf8               com/example/javac/StringExistInPoolBeforeIntern
  #30 = Utf8               java/lang/Object
  #31 = Utf8               [Ljava/lang/String;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               (Ljava/lang/String;)V
  #34 = Utf8               intern
  #35 = Utf8               ()Ljava/lang/String;
  #36 = Utf8               java/lang/System
  #37 = Utf8               out
  #38 = Utf8               Ljava/io/PrintStream;
  #39 = Utf8               println
  #40 = Utf8               (Z)V
{
  public com.example.javac.StringExistInPoolBeforeIntern();
    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 8: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: new           #2                  // class java/lang/String
         3: dup
         4: ldc           #3                  // String tomcat
         6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: astore_1
        10: aload_1
        11: invokevirtual #5                  // Method java/lang/String.intern:()Ljava/lang/String;
        14: pop
        15: ldc           #3                  // String tomcat
        17: astore_2
        18: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        21: aload_1
        22: aload_2
        23: if_acmpne     30
        26: iconst_1
        27: goto          31
        30: iconst_0
        31: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V
        34: return
      LineNumberTable:
        line 11: 0
        line 13: 10
        line 14: 15
        line 15: 18
        line 16: 34
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 30
          locals = [ class "[Ljava/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 ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "StringExistInPoolBeforeIntern.java"
  • You can see that there is a tomcat in the constant pool.

StringNotExistInPoolBeforeIntern

javac src/main/java/com/example/javac/StringNotExistInPoolBeforeIntern.java
javap -v src/main/java/com/example/javac/StringNotExistInPoolBeforeIntern.class

  Last modified 2019年4月6日; size 1187 bytes
  MD5 checksum 6d173f303b61b8f5826e54bb6ed5157c
  Compiled from "StringNotExistInPoolBeforeIntern.java"
public class com.example.javac.StringNotExistInPoolBeforeIntern
  minor version: 0
  major version: 56
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #11                         // com/example/javac/StringNotExistInPoolBeforeIntern
  super_class: #12                        // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 3
Constant pool:
   #1 = Methodref          #12.#24        // java/lang/Object."<init>":()V
   #2 = Class              #25            // java/lang/String
   #3 = String             #26            // tom
   #4 = Methodref          #2.#27         // java/lang/String."<init>":(Ljava/lang/String;)V
   #5 = String             #28            // cat
   #6 = InvokeDynamic      #0:#32         // #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
   #7 = Methodref          #2.#33         // java/lang/String.intern:()Ljava/lang/String;
   #8 = String             #34            // tomcat
   #9 = Fieldref           #35.#36        // java/lang/System.out:Ljava/io/PrintStream;
  #10 = Methodref          #21.#37        // java/io/PrintStream.println:(Z)V
  #11 = Class              #38            // com/example/javac/StringNotExistInPoolBeforeIntern
  #12 = Class              #39            // java/lang/Object
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               StackMapTable
  #20 = Class              #40            // "[Ljava/lang/String;"
  #21 = Class              #41            // java/io/PrintStream
  #22 = Utf8               SourceFile
  #23 = Utf8               StringNotExistInPoolBeforeIntern.java
  #24 = NameAndType        #13:#14        // "<init>":()V
  #25 = Utf8               java/lang/String
  #26 = Utf8               tom
  #27 = NameAndType        #13:#42        // "<init>":(Ljava/lang/String;)V
  #28 = Utf8               cat
  #29 = Utf8               BootstrapMethods
  #30 = MethodHandle       6:#43          // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #31 = String             #44            // \u0001\u0001
  #32 = NameAndType        #45:#46        // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  #33 = NameAndType        #47:#48        // intern:()Ljava/lang/String;
  #34 = Utf8               tomcat
  #35 = Class              #49            // java/lang/System
  #36 = NameAndType        #50:#51        // out:Ljava/io/PrintStream;
  #37 = NameAndType        #52:#53        // println:(Z)V
  #38 = Utf8               com/example/javac/StringNotExistInPoolBeforeIntern
  #39 = Utf8               java/lang/Object
  #40 = Utf8               [Ljava/lang/String;
  #41 = Utf8               java/io/PrintStream
  #42 = Utf8               (Ljava/lang/String;)V
  #43 = Methodref          #54.#55        // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #44 = Utf8               \u0001\u0001
  #45 = Utf8               makeConcatWithConstants
  #46 = Utf8               (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  #47 = Utf8               intern
  #48 = Utf8               ()Ljava/lang/String;
  #49 = Utf8               java/lang/System
  #50 = Utf8               out
  #51 = Utf8               Ljava/io/PrintStream;
  #52 = Utf8               println
  #53 = Utf8               (Z)V
  #54 = Class              #56            // java/lang/invoke/StringConcatFactory
  #55 = NameAndType        #45:#60        // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #56 = Utf8               java/lang/invoke/StringConcatFactory
  #57 = Class              #62            // java/lang/invoke/MethodHandles$Lookup
  #58 = Utf8               Lookup
  #59 = Utf8               InnerClasses
  #60 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #61 = Class              #63            // java/lang/invoke/MethodHandles
  #62 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #63 = Utf8               java/lang/invoke/MethodHandles
{
  public com.example.javac.StringNotExistInPoolBeforeIntern();
    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 8: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=3, args_size=1
         0: new           #2                  // class java/lang/String
         3: dup
         4: ldc           #3                  // String tom
         6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: new           #2                  // class java/lang/String
        12: dup
        13: ldc           #5                  // String cat
        15: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        18: invokedynamic #6,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
        23: astore_1
        24: aload_1
        25: invokevirtual #7                  // Method java/lang/String.intern:()Ljava/lang/String;
        28: pop
        29: ldc           #8                  // String tomcat
        31: astore_2
        32: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        35: aload_1
        36: aload_2
        37: if_acmpne     44
        40: iconst_1
        41: goto          45
        44: iconst_0
        45: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        48: return
      LineNumberTable:
        line 11: 0
        line 13: 24
        line 14: 29
        line 15: 32
        line 16: 48
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 44
          locals = [ class "[Ljava/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 ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "StringNotExistInPoolBeforeIntern.java"
InnerClasses:
  public static final #58= #57 of #61;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #30 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #31 \u0001\u0001
  • You can see that the constant pools are tom, cat, tomcat

Summary

  • When the intern method is called, if the constant pool already contains a String of equals this string object, the string in the pool is returned
  • When the intern method is called, if the constant pool does not have a String equals to this String object, add this String object to the pool and return a reference to this String object (That is, the intern method returns a reference to this String object in heap)
  • All literal strings and string-valued constant expressions are interned.

doc